blob: 36c04e96197e94fb897016b815b0f27d021c5161 [file] [log] [blame]
// Copyright 2018 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "shill/device.h"
#include <ctype.h>
#include <linux/if.h> // NOLINT - Needs typedefs from sys/socket.h.
#include <sys/socket.h>
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <base/bind.h>
#include <base/callback.h>
#include <base/check.h>
#include <base/run_loop.h>
#include <base/time/time.h>
#include <chromeos/dbus/service_constants.h>
#include <chromeos/patchpanel/dbus/fake_client.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "shill/event_dispatcher.h"
#include "shill/mock_adaptors.h"
#include "shill/mock_control.h"
#include "shill/mock_device.h"
#include "shill/mock_device_info.h"
#include "shill/mock_event_dispatcher.h"
#include "shill/mock_ipconfig.h"
#include "shill/mock_manager.h"
#include "shill/mock_metrics.h"
#include "shill/mock_portal_detector.h"
#include "shill/mock_service.h"
#include "shill/net/mock_rtnl_handler.h"
#include "shill/net/mock_time.h"
#include "shill/net/ndisc.h"
#include "shill/network/mock_network.h"
#include "shill/network/network.h"
#include "shill/portal_detector.h"
#include "shill/routing_table.h"
#include "shill/store/fake_store.h"
#include "shill/technology.h"
#include "shill/test_event_dispatcher.h"
#include "shill/testing.h"
#include "shill/tethering.h"
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::AtLeast;
using ::testing::DoAll;
using ::testing::HasSubstr;
using ::testing::Invoke;
using ::testing::InvokeWithoutArgs;
using ::testing::Mock;
using ::testing::NiceMock;
using ::testing::Ref;
using ::testing::Return;
using ::testing::ReturnRef;
using ::testing::SetArgPointee;
using ::testing::StrEq;
using ::testing::StrictMock;
namespace shill {
namespace {
MATCHER_P(IsWeakPtrTo, address, "") {
return arg.get() == address;
}
} // namespace
class TestDevice : public Device {
public:
TestDevice(Manager* manager,
const std::string& link_name,
const std::string& address,
int interface_index,
Technology technology)
: Device(manager, link_name, address, interface_index, technology),
start_stop_error_(Error::kSuccess) {
ON_CALL(*this, ShouldBringNetworkInterfaceDownAfterDisabled())
.WillByDefault(Invoke(
this,
&TestDevice::DeviceShouldBringNetworkInterfaceDownAfterDisabled));
}
~TestDevice() override = default;
void Start(const EnabledStateChangedCallback& callback) override {
callback.Run(start_stop_error_);
}
void Stop(const EnabledStateChangedCallback& callback) override {
callback.Run(start_stop_error_);
}
MOCK_METHOD(bool,
ShouldBringNetworkInterfaceDownAfterDisabled,
(),
(const, override));
MOCK_METHOD(void,
StartConnectionDiagnosticsAfterPortalDetection,
(),
(override));
bool DeviceShouldBringNetworkInterfaceDownAfterDisabled() const {
return Device::ShouldBringNetworkInterfaceDownAfterDisabled();
}
void device_set_mac_address(const std::string& mac_address) {
Device::set_mac_address(mac_address);
}
Error start_stop_error_;
};
class DeviceTest : public testing::Test {
public:
DeviceTest()
: manager_(control_interface(), dispatcher(), metrics()),
device_info_(manager()) {
manager()->set_mock_device_info(&device_info_);
DHCPProvider::GetInstance()->control_interface_ = control_interface();
DHCPProvider::GetInstance()->dispatcher_ = dispatcher();
auto client = std::make_unique<patchpanel::FakeClient>();
patchpanel_client_ = client.get();
manager_.patchpanel_client_ = std::move(client);
device_ =
new NiceMock<TestDevice>(manager(), kDeviceName, kDeviceAddress,
kDeviceInterfaceIndex, Technology::kUnknown);
auto network = std::make_unique<NiceMock<MockNetwork>>(
kDeviceInterfaceIndex, kDeviceName, Technology::kUnknown);
network_ = network.get();
device_->set_network_for_testing(std::move(network));
}
~DeviceTest() override = default;
void SetUp() override {
device_->rtnl_handler_ = &rtnl_handler_;
RoutingTable::GetInstance()->Start();
}
protected:
static const char kDeviceName[];
static const char kDeviceAddress[];
static const int kDeviceInterfaceIndex;
void OnIPv4ConfigUpdated() { device_->network()->OnIPv4ConfigUpdated(); }
void OnDHCPFailure() { device_->network()->OnDHCPFailure(); }
patchpanel::TrafficCounter CreateCounter(
const std::valarray<uint64_t>& vals,
patchpanel::TrafficCounter::Source source,
const std::string& device_name) {
EXPECT_EQ(4, vals.size());
patchpanel::TrafficCounter counter;
counter.set_rx_bytes(vals[0]);
counter.set_tx_bytes(vals[1]);
counter.set_rx_packets(vals[2]);
counter.set_tx_packets(vals[3]);
counter.set_source(source);
counter.set_device(device_name);
return counter;
}
void SelectService(scoped_refptr<MockService> service) {
if (service) {
EXPECT_CALL(*service,
SetAttachedNetwork(IsWeakPtrTo(device_->network())));
}
device_->SelectService(service);
}
MockPortalDetector* SetMockPortalDetector() {
auto mock_portal_detector =
std::make_unique<StrictMock<MockPortalDetector>>();
MockPortalDetector* mock_portal_detectorp = mock_portal_detector.get();
EXPECT_CALL(*mock_portal_detectorp, IsInProgress())
.WillRepeatedly(Return(false));
device_->portal_detector_ = std::move(mock_portal_detector);
return mock_portal_detectorp;
}
DeviceMockAdaptor* GetDeviceMockAdaptor() {
return static_cast<DeviceMockAdaptor*>(device_->adaptor_.get());
}
PortalDetector* GetPortalDetector() {
return device_->portal_detector_.get();
}
MockControl* control_interface() { return &control_interface_; }
EventDispatcher* dispatcher() { return &dispatcher_; }
MockMetrics* metrics() { return &metrics_; }
MockManager* manager() { return &manager_; }
void TriggerConnectionUpdate() {
EXPECT_CALL(*network_, IsConnected()).WillRepeatedly(Return(true));
network_->set_ipconfig(
std::make_unique<MockIPConfig>(control_interface(), kDeviceName));
device_->OnConnectionUpdated();
device_->OnIPConfigsPropertyUpdated();
}
static ManagerProperties MakePortalProperties() {
ManagerProperties props;
props.portal_http_url = PortalDetector::kDefaultHttpUrl;
props.portal_https_url = PortalDetector::kDefaultHttpsUrl;
props.portal_fallback_http_urls = std::vector<std::string>(
PortalDetector::kDefaultFallbackHttpUrls.begin(),
PortalDetector::kDefaultFallbackHttpUrls.end());
return props;
}
NiceMock<MockControl> control_interface_;
EventDispatcherForTest dispatcher_;
NiceMock<MockMetrics> metrics_;
NiceMock<MockManager> manager_;
scoped_refptr<TestDevice> device_;
NiceMock<MockDeviceInfo> device_info_;
StrictMock<MockRTNLHandler> rtnl_handler_;
patchpanel::FakeClient* patchpanel_client_;
MockNetwork* network_; // owned by |device_|
};
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<std::string>());
EXPECT_EQ(props[kNameProperty].Get<std::string>(), std::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.
device_->mutable_store()->SetAnyProperty(kAddressProperty,
brillo::Any(std::string()), &error);
EXPECT_EQ(Error::kInvalidArguments, error.type());
}
TEST_F(DeviceTest, ClearReadOnlyProperty) {
Error error;
device_->mutable_store()->SetAnyProperty(kAddressProperty,
brillo::Any(std::string()), &error);
EXPECT_EQ(Error::kInvalidArguments, error.type());
}
TEST_F(DeviceTest, ClearReadOnlyDerivedProperty) {
Error error;
device_->mutable_store()->SetAnyProperty(kIPConfigsProperty,
brillo::Any(Strings()), &error);
EXPECT_EQ(Error::kInvalidArguments, error.type());
}
TEST_F(DeviceTest, Load) {
device_->enabled_persistent_ = false;
FakeStore storage;
const auto id = device_->GetStorageIdentifier();
storage.SetBool(id, Device::kStoragePowered, true);
EXPECT_TRUE(device_->Load(&storage));
EXPECT_TRUE(device_->enabled_persistent());
}
TEST_F(DeviceTest, Save) {
device_->enabled_persistent_ = true;
FakeStore storage;
EXPECT_TRUE(device_->Save(&storage));
const auto id = device_->GetStorageIdentifier();
bool powered = false;
EXPECT_TRUE(storage.GetBool(id, Device::kStoragePowered, &powered));
EXPECT_TRUE(powered);
}
TEST_F(DeviceTest, SelectedService) {
EXPECT_EQ(nullptr, device_->selected_service_);
device_->SetServiceState(Service::kStateAssociating);
scoped_refptr<MockService> service(new StrictMock<MockService>(manager()));
SelectService(service);
EXPECT_EQ(device_->selected_service_, service);
EXPECT_CALL(*service, SetState(Service::kStateConfiguring));
device_->SetServiceState(Service::kStateConfiguring);
EXPECT_CALL(*service, SetFailure(Service::kFailureOutOfRange));
device_->SetServiceFailure(Service::kFailureOutOfRange);
// Service should be returned to "Idle" state
EXPECT_CALL(*service, state()).WillOnce(Return(Service::kStateUnknown));
EXPECT_CALL(*service, SetState(Service::kStateIdle));
EXPECT_CALL(*service, SetAttachedNetwork(IsWeakPtrTo(nullptr)));
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, SetAttachedNetwork(IsWeakPtrTo(nullptr)));
SelectService(nullptr);
}
TEST_F(DeviceTest, ResetConnection) {
EXPECT_EQ(nullptr, device_->selected_service_);
device_->SetServiceState(Service::kStateAssociating);
scoped_refptr<MockService> service(new StrictMock<MockService>(manager()));
SelectService(service);
EXPECT_EQ(device_->selected_service_, service);
// ResetConnection() should drop the connection and the selected service,
// but should not change the service state.
EXPECT_CALL(*service, SetState(_)).Times(0);
EXPECT_CALL(*service, SetAttachedNetwork(IsWeakPtrTo(nullptr)));
device_->ResetConnection();
EXPECT_EQ(nullptr, device_->selected_service_);
}
TEST_F(DeviceTest, NetworkFailure) {
scoped_refptr<MockService> service(new StrictMock<MockService>(manager()));
SelectService(service);
EXPECT_CALL(*service, DisconnectWithFailure(Service::kFailureDHCP, _,
HasSubstr("OnIPConfigFailure")));
device_->OnNetworkStopped(/*is_failure=*/true);
}
TEST_F(DeviceTest, ConnectionUpdated) {
scoped_refptr<MockService> service(new StrictMock<MockService>(manager()));
SelectService(service);
EXPECT_CALL(*service, IsConnected(nullptr))
.WillOnce(Return(false))
.WillRepeatedly(Return(true));
EXPECT_CALL(*service, IsPortalDetectionDisabled())
.WillRepeatedly(Return(true));
EXPECT_CALL(*service, SetState(Service::kStateConnected));
EXPECT_CALL(*service, SetState(Service::kStateOnline));
EXPECT_CALL(*GetDeviceMockAdaptor(),
EmitRpcIdentifierArrayChanged(
kIPConfigsProperty,
std::vector<RpcIdentifier>{IPConfigMockAdaptor::kRpcId}));
TriggerConnectionUpdate();
}
TEST_F(DeviceTest, ConnectionUpdatedAlreadyOnline) {
// The service is already Online and selected, so it should not transition
// back to Connected.
scoped_refptr<MockService> service(new StrictMock<MockService>(manager()));
SelectService(service);
EXPECT_CALL(*service, SetState(Service::kStateConnected)).Times(0);
EXPECT_CALL(*service, IsConnected(nullptr)).WillRepeatedly(Return(true));
EXPECT_CALL(*service, IsPortalDetectionDisabled())
.WillRepeatedly(Return(true));
// Successful portal (non-)detection forces the service Online.
EXPECT_CALL(*service, SetState(Service::kStateOnline));
EXPECT_CALL(*GetDeviceMockAdaptor(),
EmitRpcIdentifierArrayChanged(
kIPConfigsProperty,
std::vector<RpcIdentifier>{IPConfigMockAdaptor::kRpcId}));
TriggerConnectionUpdate();
}
TEST_F(DeviceTest, ConnectionUpdatedSuccessNoSelectedService) {
// Make sure shill doesn't crash if a service is disabled immediately after
// Network is connected (selected_service_ is nullptr in this case).
SelectService(nullptr);
TriggerConnectionUpdate();
}
TEST_F(DeviceTest, SetEnabledNonPersistent) {
EXPECT_FALSE(device_->enabled_);
EXPECT_FALSE(device_->enabled_pending_);
device_->enabled_persistent_ = false;
Error error;
SetEnabledSync(device_.get(), true, false, &error);
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;
SetEnabledSync(device_.get(), true, false, &error);
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;
SetEnabledSync(device_.get(), true, false, &error);
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;
SetEnabledSync(device_.get(), false, false, &error);
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;
SetEnabledSync(device_.get(), false, false, &error);
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;
Error error;
SetEnabledSync(device_.get(), true, true, &error);
EXPECT_TRUE(device_->enabled_persistent_);
EXPECT_TRUE(device_->enabled_pending_);
// Enable while already enabled (but not persisted).
error.Populate(Error::kOperationInitiated);
device_->enabled_persistent_ = false;
device_->enabled_pending_ = true;
device_->enabled_ = true;
SetEnabledSync(device_.get(), true, true, &error);
EXPECT_TRUE(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;
SetEnabledSync(device_.get(), true, true, &error);
EXPECT_TRUE(device_->enabled_persistent_);
EXPECT_FALSE(device_->enabled_pending_);
EXPECT_TRUE(device_->enabled_);
EXPECT_EQ(Error::kOperationFailed, error.type());
// Disable while already disabled (persisted).
error.Populate(Error::kOperationInitiated);
device_->enabled_ = false;
SetEnabledSync(device_.get(), false, true, &error);
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;
SetEnabledSync(device_.get(), false, true, &error);
EXPECT_FALSE(device_->enabled_persistent_);
EXPECT_TRUE(device_->enabled_pending_);
EXPECT_FALSE(device_->enabled_);
EXPECT_EQ(Error::kOperationFailed, error.type());
// Disable while already disabled (but not persisted).
error.Reset();
device_->enabled_persistent_ = true;
device_->enabled_pending_ = false;
device_->enabled_ = false;
SetEnabledSync(device_.get(), false, true, &error);
EXPECT_FALSE(device_->enabled_persistent_);
EXPECT_FALSE(device_->enabled_pending_);
EXPECT_FALSE(device_->enabled_);
EXPECT_TRUE(error.IsSuccess());
}
TEST_F(DeviceTest, Start) {
EXPECT_FALSE(device_->enabled_);
EXPECT_FALSE(device_->enabled_pending_);
device_->SetEnabled(true);
EXPECT_TRUE(device_->enabled_pending_);
EXPECT_TRUE(device_->enabled_);
}
TEST_F(DeviceTest, StartFailure) {
EXPECT_FALSE(device_->enabled_);
EXPECT_FALSE(device_->enabled_pending_);
device_->start_stop_error_.Populate(Error::kOperationFailed);
device_->SetEnabled(true);
EXPECT_FALSE(device_->enabled_pending_);
EXPECT_FALSE(device_->enabled_);
}
TEST_F(DeviceTest, Stop) {
device_->enabled_ = true;
device_->enabled_pending_ = true;
scoped_refptr<MockService> service(new NiceMock<MockService>(manager()));
SelectService(service);
EXPECT_CALL(*service, state())
.WillRepeatedly(Return(Service::kStateConnected));
EXPECT_CALL(*GetDeviceMockAdaptor(),
EmitBoolChanged(kPoweredProperty, false));
EXPECT_CALL(rtnl_handler_, SetInterfaceFlags(_, 0, IFF_UP));
EXPECT_CALL(*service, SetAttachedNetwork(IsWeakPtrTo(nullptr)));
EXPECT_CALL(*network_, Stop());
device_->SetEnabled(false);
EXPECT_EQ(nullptr, device_->selected_service_);
}
TEST_F(DeviceTest, StopWithFixedIpParams) {
device_->network()->set_fixed_ip_params_for_testing(true);
device_->enabled_ = true;
device_->enabled_pending_ = true;
scoped_refptr<MockService> service(new NiceMock<MockService>(manager()));
SelectService(service);
EXPECT_CALL(*service, state())
.WillRepeatedly(Return(Service::kStateConnected));
EXPECT_CALL(*GetDeviceMockAdaptor(),
EmitBoolChanged(kPoweredProperty, false));
EXPECT_CALL(rtnl_handler_, SetInterfaceFlags(_, _, _)).Times(0);
EXPECT_CALL(*service, SetAttachedNetwork(IsWeakPtrTo(nullptr)));
EXPECT_CALL(*network_, Stop());
device_->SetEnabled(false);
EXPECT_EQ(nullptr, device_->selected_service_);
}
TEST_F(DeviceTest, StopWithNetworkInterfaceDisabledAfterward) {
device_->enabled_ = true;
device_->enabled_pending_ = true;
scoped_refptr<MockService> service(new NiceMock<MockService>(manager()));
SelectService(service);
EXPECT_CALL(*device_, ShouldBringNetworkInterfaceDownAfterDisabled())
.WillRepeatedly(Return(true));
EXPECT_CALL(*service, state())
.WillRepeatedly(Return(Service::kStateConnected));
EXPECT_CALL(*GetDeviceMockAdaptor(),
EmitBoolChanged(kPoweredProperty, false));
EXPECT_CALL(*service, SetAttachedNetwork(IsWeakPtrTo(nullptr)));
EXPECT_CALL(rtnl_handler_, SetInterfaceFlags(_, 0, IFF_UP));
EXPECT_CALL(*network_, Stop());
device_->SetEnabled(false);
EXPECT_EQ(nullptr, device_->selected_service_);
}
TEST_F(DeviceTest, StartProhibited) {
DeviceRefPtr device(new TestDevice(manager(), kDeviceName, kDeviceAddress,
kDeviceInterfaceIndex, Technology::kWiFi));
{
Error error;
manager()->SetProhibitedTechnologies("wifi", &error);
EXPECT_TRUE(error.IsSuccess());
}
device->SetEnabled(true);
EXPECT_FALSE(device->enabled_pending());
{
Error error;
manager()->SetProhibitedTechnologies("", &error);
EXPECT_TRUE(error.IsSuccess());
}
device->SetEnabled(true);
EXPECT_TRUE(device->enabled_pending());
}
TEST_F(DeviceTest, Reset) {
base::RunLoop run_loop;
Error e;
device_->Reset(
base::BindRepeating(&SetErrorAndReturn, run_loop.QuitClosure(), &e));
run_loop.Run();
EXPECT_EQ(Error::kNotImplemented, e.type());
}
TEST_F(DeviceTest, Resume) {
EXPECT_CALL(*network_, RenewDHCPLease());
EXPECT_CALL(*network_, InvalidateIPv6Config());
device_->OnAfterResume();
}
TEST_F(DeviceTest, IsConnectedViaTether) {
EXPECT_FALSE(device_->IsConnectedViaTether());
// An empty ipconfig doesn't mean we're tethered.
network_->set_ipconfig(
std::make_unique<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);
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);
EXPECT_FALSE(device_->IsConnectedViaTether());
}
TEST_F(DeviceTest, AvailableIPConfigs) {
EXPECT_EQ(std::vector<RpcIdentifier>(), device_->AvailableIPConfigs(nullptr));
network_->set_ipconfig(
std::make_unique<IPConfig>(control_interface(), kDeviceName));
EXPECT_EQ(std::vector<RpcIdentifier>{IPConfigMockAdaptor::kRpcId},
device_->AvailableIPConfigs(nullptr));
network_->set_ip6config(
std::make_unique<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());
network_->set_ipconfig(nullptr);
EXPECT_EQ(std::vector<RpcIdentifier>{IPConfigMockAdaptor::kRpcId},
device_->AvailableIPConfigs(nullptr));
network_->set_ip6config(nullptr);
EXPECT_EQ(std::vector<RpcIdentifier>(), device_->AvailableIPConfigs(nullptr));
}
TEST_F(DeviceTest, SetMacAddress) {
constexpr char mac_address[] = "abcdefabcdef";
EXPECT_CALL(*GetDeviceMockAdaptor(),
EmitStringChanged(kAddressProperty, mac_address));
EXPECT_NE(mac_address, device_->mac_address());
device_->device_set_mac_address(mac_address);
EXPECT_EQ(mac_address, device_->mac_address());
}
TEST_F(DeviceTest, FetchTrafficCounters) {
auto source0 = patchpanel::TrafficCounter::CHROME;
auto source1 = patchpanel::TrafficCounter::USER;
std::valarray<uint64_t> counter_arr0{2842, 1243, 240598, 43095};
std::valarray<uint64_t> counter_arr1{4554666, 43543, 5999, 500000};
patchpanel::TrafficCounter counter0 =
CreateCounter(counter_arr0, source0, kDeviceName);
patchpanel::TrafficCounter counter1 =
CreateCounter(counter_arr1, source1, kDeviceName);
std::vector<patchpanel::TrafficCounter> counters{counter0, counter1};
patchpanel_client_->set_stored_traffic_counters(counters);
EXPECT_EQ(nullptr, device_->selected_service_);
scoped_refptr<MockService> service0(new NiceMock<MockService>(manager()));
EXPECT_TRUE(service0->traffic_counter_snapshot_.empty());
EXPECT_TRUE(service0->current_traffic_counters_.empty());
SelectService(service0);
EXPECT_EQ(service0, device_->selected_service_);
EXPECT_TRUE(service0->current_traffic_counters_.empty());
EXPECT_EQ(2, service0->traffic_counter_snapshot_.size());
for (size_t i = 0; i < Service::kTrafficCounterArraySize; i++) {
EXPECT_EQ(counter_arr0[i], service0->traffic_counter_snapshot_[source0][i]);
EXPECT_EQ(counter_arr1[i], service0->traffic_counter_snapshot_[source1][i]);
}
std::valarray<uint64_t> counter_diff0{12, 98, 34, 76};
std::valarray<uint64_t> counter_diff1{324534, 23434, 785676, 256};
std::valarray<uint64_t> new_total0 = counter_arr0 + counter_diff0;
std::valarray<uint64_t> new_total1 = counter_arr1 + counter_diff1;
counter0 = CreateCounter(new_total0, source0, kDeviceName);
counter1 = CreateCounter(new_total1, source1, kDeviceName);
counters = {counter0, counter1};
patchpanel_client_->set_stored_traffic_counters(counters);
scoped_refptr<MockService> service1(new NiceMock<MockService>(manager()));
EXPECT_CALL(*service0, SetAttachedNetwork(IsWeakPtrTo(nullptr)));
SelectService(service1);
EXPECT_EQ(service1, device_->selected_service_);
for (size_t i = 0; i < Service::kTrafficCounterArraySize; i++) {
EXPECT_EQ(counter_diff0[i],
service0->current_traffic_counters_[source0][i]);
EXPECT_EQ(counter_diff1[i],
service0->current_traffic_counters_[source1][i]);
EXPECT_EQ(new_total0[i], service1->traffic_counter_snapshot_[source0][i]);
EXPECT_EQ(new_total1[i], service1->traffic_counter_snapshot_[source1][i]);
}
EXPECT_TRUE(service1->current_traffic_counters_.empty());
}
class DevicePortalDetectionTest : public DeviceTest {
public:
DevicePortalDetectionTest()
: service_(new StrictMock<MockService>(manager())) {
ON_CALL(*network_, IsConnected()).WillByDefault(Return(true));
}
~DevicePortalDetectionTest() override = default;
void SetUp() override {
DeviceTest::SetUp();
EXPECT_CALL(*service_, SetProbeUrl(_)).Times(AnyNumber());
SelectService(service_);
}
void TearDown() override { device_->portal_detector_.reset(); }
protected:
static const int kPortalAttempts;
bool UpdatePortalDetector(bool restart = false) {
return device_->UpdatePortalDetector(restart);
}
void StopPortalDetection() { device_->StopPortalDetection(); }
void PortalDetectorCallback(const PortalDetector::Result& result) {
device_->PortalDetectorCallback(result);
}
scoped_refptr<MockService> service_;
};
const int DevicePortalDetectionTest::kPortalAttempts = 2;
TEST_F(DevicePortalDetectionTest, NoSelectedService) {
device_->set_selected_service_for_testing(nullptr);
EXPECT_CALL(*service_, IsPortalDetectionDisabled()).Times(0);
EXPECT_CALL(*service_, IsConnected(nullptr)).Times(0);
EXPECT_CALL(*service_, SetState(Service::kStateOnline)).Times(0);
EXPECT_FALSE(UpdatePortalDetector(true));
EXPECT_FALSE(UpdatePortalDetector(false));
ASSERT_EQ(nullptr, GetPortalDetector());
}
TEST_F(DevicePortalDetectionTest, NoConnection) {
EXPECT_CALL(*network_, IsConnected()).WillRepeatedly(Return(false));
EXPECT_CALL(*service_, IsPortalDetectionDisabled()).Times(0);
EXPECT_CALL(*service_, IsConnected(nullptr)).Times(0);
EXPECT_CALL(*service_, SetState(Service::kStateOnline)).Times(0);
EXPECT_FALSE(UpdatePortalDetector(true));
EXPECT_FALSE(UpdatePortalDetector(false));
ASSERT_EQ(nullptr, GetPortalDetector());
}
TEST_F(DevicePortalDetectionTest, PortalDetectionDisabled) {
EXPECT_CALL(*service_, IsPortalDetectionDisabled())
.WillRepeatedly(Return(true));
EXPECT_CALL(*service_, IsConnected(nullptr)).WillRepeatedly(Return(true));
EXPECT_CALL(*service_, SetState(Service::kStateOnline)).Times(2);
EXPECT_FALSE(UpdatePortalDetector(true));
EXPECT_FALSE(UpdatePortalDetector(false));
ASSERT_EQ(nullptr, GetPortalDetector());
}
TEST_F(DevicePortalDetectionTest, PortalDetectionInProgress_DoNotForceRestart) {
auto mock_portal_detector = SetMockPortalDetector();
EXPECT_CALL(*mock_portal_detector, IsInProgress())
.WillRepeatedly(Return(true));
EXPECT_CALL(*service_, IsPortalDetectionDisabled())
.WillRepeatedly(Return(false));
EXPECT_CALL(*service_, IsConnected(nullptr)).WillRepeatedly(Return(true));
EXPECT_CALL(*service_, SetState(Service::kStateOnline)).Times(0);
EXPECT_TRUE(UpdatePortalDetector(false));
ASSERT_EQ(mock_portal_detector, GetPortalDetector());
}
TEST_F(DevicePortalDetectionTest, PortalDetectionInProgress_ForceRestart) {
const IPAddress ip_addr = IPAddress("1.2.3.4");
const ManagerProperties props = MakePortalProperties();
const std::vector<std::string> kDNSServers;
auto mock_portal_detector = SetMockPortalDetector();
EXPECT_CALL(*mock_portal_detector, IsInProgress())
.WillRepeatedly(Return(true));
EXPECT_CALL(manager_, GetProperties()).WillRepeatedly(ReturnRef(props));
EXPECT_CALL(*service_, IsPortalDetectionDisabled())
.WillRepeatedly(Return(false));
EXPECT_CALL(*service_, IsConnected(nullptr)).WillRepeatedly(Return(true));
EXPECT_CALL(*network_, local()).WillRepeatedly(Return(ip_addr));
EXPECT_CALL(*network_, dns_servers()).WillRepeatedly(Return(kDNSServers));
EXPECT_TRUE(UpdatePortalDetector(true));
ASSERT_NE(mock_portal_detector, GetPortalDetector());
}
TEST_F(DevicePortalDetectionTest, PortalDetectionBadUrl) {
const IPAddress ip_addr = IPAddress("1.2.3.4");
const ManagerProperties props;
const std::vector<std::string> kDNSServers;
EXPECT_CALL(manager_, GetProperties()).WillRepeatedly(ReturnRef(props));
EXPECT_CALL(*service_, IsConnected(nullptr)).WillRepeatedly(Return(true));
EXPECT_CALL(*service_, IsPortalDetectionDisabled())
.WillRepeatedly(Return(false));
EXPECT_CALL(*service_, SetState(Service::kStateOnline));
EXPECT_CALL(*network_, local()).WillRepeatedly(Return(ip_addr));
EXPECT_CALL(*network_, dns_servers()).WillRepeatedly(Return(kDNSServers));
EXPECT_FALSE(UpdatePortalDetector());
ASSERT_EQ(nullptr, GetPortalDetector());
}
TEST_F(DevicePortalDetectionTest, PortalDetectionStart) {
const IPAddress ip_addr = IPAddress("1.2.3.4");
const auto props = MakePortalProperties();
const std::vector<std::string> kDNSServers;
EXPECT_CALL(manager_, GetProperties()).WillRepeatedly(ReturnRef(props));
EXPECT_CALL(*service_, IsPortalDetectionDisabled())
.WillRepeatedly(Return(false));
EXPECT_CALL(*service_, IsConnected(nullptr)).WillRepeatedly(Return(true));
EXPECT_CALL(*service_, SetState(Service::kStateOnline)).Times(0);
EXPECT_CALL(*network_, local()).WillRepeatedly(Return(ip_addr));
EXPECT_CALL(*network_, dns_servers()).WillRepeatedly(Return(kDNSServers));
EXPECT_TRUE(UpdatePortalDetector());
ASSERT_NE(nullptr, GetPortalDetector());
StopPortalDetection();
}
TEST_F(DevicePortalDetectionTest, PortalDetectionStartIPv6) {
const IPAddress ip_addr = IPAddress("2001:db8:0:1::1");
const auto props = MakePortalProperties();
const std::vector<std::string> kDNSServers;
EXPECT_CALL(manager_, GetProperties()).WillRepeatedly(ReturnRef(props));
EXPECT_CALL(*service_, IsPortalDetectionDisabled())
.WillRepeatedly(Return(false));
EXPECT_CALL(*service_, IsConnected(nullptr)).WillRepeatedly(Return(true));
EXPECT_CALL(*service_, SetState(Service::kStateOnline)).Times(0);
EXPECT_CALL(*network_, local()).WillRepeatedly(Return(ip_addr));
EXPECT_CALL(*network_, dns_servers()).WillRepeatedly(Return(kDNSServers));
EXPECT_TRUE(UpdatePortalDetector());
ASSERT_NE(nullptr, GetPortalDetector());
StopPortalDetection();
}
TEST_F(DevicePortalDetectionTest, PortalRetryAfterDetectionFailure) {
const int kFailureStatusCode = 204;
const auto ip_addr = IPAddress("1.2.3.4");
const std::vector<std::string> dns_list = {"8.8.8.8", "8.8.4.4"};
const auto props = MakePortalProperties();
PortalDetector::Result result;
result.http_phase = PortalDetector::Phase::kConnection,
result.http_status = PortalDetector::Status::kFailure;
result.http_status_code = kFailureStatusCode;
result.https_phase = PortalDetector::Phase::kContent;
result.https_status = PortalDetector::Status::kSuccess;
result.num_attempts = kPortalAttempts;
const auto attempt_delay = base::Milliseconds(13450);
EXPECT_CALL(manager_, GetProperties()).WillRepeatedly(ReturnRef(props));
EXPECT_CALL(*service_, IsConnected(nullptr)).WillOnce(Return(true));
EXPECT_CALL(*service_,
SetPortalDetectionFailure(kPortalDetectionPhaseConnection,
kPortalDetectionStatusFailure,
kFailureStatusCode));
EXPECT_CALL(*service_, SetState(Service::kStateNoConnectivity));
EXPECT_CALL(*metrics(), SendEnumToUMA(_, Technology(Technology::kUnknown), _))
.Times(AnyNumber());
EXPECT_CALL(*metrics(),
SendEnumToUMA(Metrics::kMetricPortalResult,
Technology(Technology::kUnknown),
Metrics::kPortalResultConnectionFailure));
EXPECT_CALL(
*metrics(),
SendToUMA("Network.Shill.Unknown.PortalAttemptsToOnline", _, _, _, _))
.Times(0);
EXPECT_CALL(*network_, local()).WillRepeatedly(Return(ip_addr));
EXPECT_CALL(*network_, dns_servers()).WillRepeatedly(Return(dns_list));
EXPECT_CALL(*device_, StartConnectionDiagnosticsAfterPortalDetection());
MockPortalDetector* portal_detector = SetMockPortalDetector();
EXPECT_CALL(*portal_detector, GetNextAttemptDelay())
.WillOnce(Return(attempt_delay));
EXPECT_CALL(*portal_detector,
Start(_, kDeviceName, ip_addr, dns_list, _, attempt_delay))
.WillOnce(Return(true));
PortalDetectorCallback(result);
}
TEST_F(DevicePortalDetectionTest, PortalDetectionSuccess) {
EXPECT_CALL(*service_, IsConnected(nullptr)).WillOnce(Return(true));
EXPECT_CALL(*service_, SetPortalDetectionFailure(_, _, _)).Times(0);
EXPECT_CALL(*service_, SetState(Service::kStateOnline));
EXPECT_CALL(*metrics(), SendEnumToUMA(_, Technology(Technology::kUnknown), _))
.Times(AnyNumber());
EXPECT_CALL(*metrics(), SendEnumToUMA(Metrics::kMetricPortalResult,
Technology(Technology::kUnknown),
Metrics::kPortalResultSuccess));
EXPECT_CALL(*metrics(),
SendToUMA(Metrics::kMetricPortalAttemptsToOnline,
Technology(Technology::kUnknown), kPortalAttempts));
PortalDetector::Result result;
result.http_phase = PortalDetector::Phase::kContent;
result.http_status = PortalDetector::Status::kSuccess;
result.https_phase = PortalDetector::Phase::kContent;
result.https_status = PortalDetector::Status::kSuccess;
result.num_attempts = kPortalAttempts;
PortalDetectorCallback(result);
}
// The first portal detection attempt is inconclusive and the next portal
// detection attempt fails, causing the Service State to be set to 'online'
// since portal detection did not run.
TEST_F(DevicePortalDetectionTest, NextAttemptFails) {
const IPAddress ip_addr = IPAddress("1.2.3.4");
const std::vector<std::string> dns_list = {"8.8.8.8", "8.8.4.4"};
const auto props = MakePortalProperties();
const auto attempt_delay = base::Milliseconds(13450);
EXPECT_CALL(manager_, GetProperties()).WillRepeatedly(ReturnRef(props));
EXPECT_CALL(*service_, IsConnected(nullptr)).WillRepeatedly(Return(true));
// If portal detection attempts fail, default to optimistically assuming that
// the Service is 'online'.
EXPECT_CALL(*service_, SetState(Service::kStateOnline));
EXPECT_CALL(*service_, SetPortalDetectionFailure(
StrEq(kPortalDetectionPhaseDns),
StrEq(kPortalDetectionStatusTimeout), 0));
EXPECT_CALL(*network_, local()).WillRepeatedly(Return(ip_addr));
EXPECT_CALL(*network_, dns_servers()).WillRepeatedly(Return(dns_list));
MockPortalDetector* portal_detector = SetMockPortalDetector();
EXPECT_CALL(*portal_detector, GetNextAttemptDelay())
.WillOnce(Return(attempt_delay));
// The second portal detection attempt fails immediately, forcing the Device
// to assume the Service state is 'online'.
EXPECT_CALL(*portal_detector,
Start(_, kDeviceName, ip_addr, dns_list, _, attempt_delay))
.WillOnce(Return(false));
// First result indicating no connectivity and triggering a new portal
// detection attempt.
PortalDetector::Result result;
result.http_phase = PortalDetector::Phase::kDNS,
result.http_status = PortalDetector::Status::kTimeout;
result.https_phase = PortalDetector::Phase::kDNS;
result.https_status = PortalDetector::Status::kTimeout;
PortalDetectorCallback(result);
}
TEST_F(DevicePortalDetectionTest, ScheduleNextDetectionAttempt) {
const IPAddress ip_addr = IPAddress("1.2.3.4");
const std::vector<std::string> dns_list = {"8.8.8.8", "8.8.4.4"};
const auto props = MakePortalProperties();
const auto attempt_delay = base::Milliseconds(13450);
EXPECT_CALL(manager_, GetProperties()).WillRepeatedly(ReturnRef(props));
EXPECT_CALL(*service_, IsConnected(nullptr)).WillRepeatedly(Return(true));
// If portal detection attempts run successfully and do not validate the
// network, the Service state does not become 'online'.
EXPECT_CALL(*service_, SetState(Service::kStateNoConnectivity));
EXPECT_CALL(*service_, SetPortalDetectionFailure(
StrEq(kPortalDetectionPhaseDns),
StrEq(kPortalDetectionStatusTimeout), 0));
EXPECT_CALL(*network_, local()).WillRepeatedly(Return(ip_addr));
EXPECT_CALL(*network_, dns_servers()).WillRepeatedly(Return(dns_list));
MockPortalDetector* portal_detector = SetMockPortalDetector();
EXPECT_CALL(*portal_detector, GetNextAttemptDelay())
.WillOnce(Return(attempt_delay));
// The second portal detection attempt succeeds.
EXPECT_CALL(*portal_detector,
Start(_, kDeviceName, ip_addr, dns_list, _, attempt_delay))
.WillOnce(Return(true));
// First result indicating no connectivity and triggering a new portal
// detection attempt.
PortalDetector::Result result;
result.http_phase = PortalDetector::Phase::kDNS,
result.http_status = PortalDetector::Status::kTimeout;
result.https_phase = PortalDetector::Phase::kDNS;
result.https_status = PortalDetector::Status::kTimeout;
PortalDetectorCallback(result);
}
TEST_F(DevicePortalDetectionTest, RestartPortalDetection) {
const IPAddress ip_addr = IPAddress("1.2.3.4");
const std::vector<std::string> kDNSServers = {"8.8.8.8", "8.8.4.4"};
auto attempt_delay = base::Milliseconds(13450);
const auto props = MakePortalProperties();
EXPECT_CALL(manager_, GetProperties()).WillRepeatedly(ReturnRef(props));
EXPECT_CALL(*service_, IsConnected(nullptr)).WillRepeatedly(Return(true));
EXPECT_CALL(*network_, local()).WillRepeatedly(Return(ip_addr));
EXPECT_CALL(*network_, dns_servers()).WillRepeatedly(Return(kDNSServers));
MockPortalDetector* portal_detector = SetMockPortalDetector();
for (int i = 0; i < 10; i++) {
EXPECT_CALL(*portal_detector, GetNextAttemptDelay())
.WillOnce(Return(attempt_delay));
EXPECT_CALL(*portal_detector,
Start(_, kDeviceName, ip_addr, kDNSServers, _, attempt_delay))
.WillOnce(Return(true));
EXPECT_CALL(*service_, SetState(Service::kStateNoConnectivity));
EXPECT_CALL(*service_, SetPortalDetectionFailure(
StrEq(kPortalDetectionPhaseDns),
StrEq(kPortalDetectionStatusTimeout), 0));
PortalDetector::Result result;
result.http_phase = PortalDetector::Phase::kDNS,
result.http_status = PortalDetector::Status::kTimeout;
result.https_phase = PortalDetector::Phase::kDNS;
result.https_status = PortalDetector::Status::kTimeout;
PortalDetectorCallback(result);
attempt_delay += base::Milliseconds(5678);
}
}
TEST_F(DevicePortalDetectionTest, CancelledOnSelectService) {
SetMockPortalDetector();
EXPECT_CALL(*service_, state()).WillOnce(Return(Service::kStateIdle));
EXPECT_CALL(*service_, SetState(_));
EXPECT_CALL(*service_, SetAttachedNetwork(IsWeakPtrTo(nullptr)));
SelectService(nullptr);
EXPECT_FALSE(GetPortalDetector());
}
TEST_F(DevicePortalDetectionTest, PortalDetectionDNSFailure) {
const int kFailureStatusCode = 204;
const auto ip_addr = IPAddress("1.2.3.4");
const std::vector<std::string> dns_list = {"8.8.8.8", "8.8.4.4"};
const auto props = MakePortalProperties();
PortalDetector::Result result;
result.http_phase = PortalDetector::Phase::kDNS,
result.http_status = PortalDetector::Status::kFailure;
result.http_status_code = kFailureStatusCode;
result.https_phase = PortalDetector::Phase::kContent;
result.https_status = PortalDetector::Status::kFailure;
result.num_attempts = kPortalAttempts;
const auto attempt_delay = base::Milliseconds(13450);
EXPECT_CALL(manager_, GetProperties()).WillRepeatedly(ReturnRef(props));
EXPECT_CALL(*service_, IsConnected(nullptr)).WillRepeatedly(Return(true));
EXPECT_CALL(*service_,
SetPortalDetectionFailure(kPortalDetectionPhaseDns,
kPortalDetectionStatusFailure,
kFailureStatusCode));
EXPECT_CALL(*service_, SetState(Service::kStateNoConnectivity));
EXPECT_CALL(*network_, local()).WillRepeatedly(Return(ip_addr));
EXPECT_CALL(*network_, dns_servers()).WillRepeatedly(Return(dns_list));
EXPECT_CALL(*device_, StartConnectionDiagnosticsAfterPortalDetection());
MockPortalDetector* portal_detector = SetMockPortalDetector();
EXPECT_CALL(*portal_detector, GetNextAttemptDelay())
.WillRepeatedly(Return(attempt_delay));
EXPECT_CALL(*portal_detector,
Start(_, kDeviceName, ip_addr, dns_list, _, attempt_delay))
.WillRepeatedly(Return(true));
PortalDetectorCallback(result);
}
TEST_F(DevicePortalDetectionTest, PortalDetectionDNSTimeout) {
const auto ip_addr = IPAddress("1.2.3.4");
const std::vector<std::string> dns_list = {"8.8.8.8", "8.8.4.4"};
const auto props = MakePortalProperties();
PortalDetector::Result result;
result.http_phase = PortalDetector::Phase::kDNS,
result.http_status = PortalDetector::Status::kTimeout;
result.http_status_code = 0;
result.https_phase = PortalDetector::Phase::kContent;
result.https_status = PortalDetector::Status::kFailure;
result.num_attempts = kPortalAttempts;
const auto attempt_delay = base::Milliseconds(13450);
EXPECT_CALL(manager_, GetProperties()).WillRepeatedly(ReturnRef(props));
EXPECT_CALL(*service_, IsConnected(nullptr)).WillRepeatedly(Return(true));
EXPECT_CALL(*service_,
SetPortalDetectionFailure(kPortalDetectionPhaseDns,
kPortalDetectionStatusTimeout, 0));
EXPECT_CALL(*service_, SetState(Service::kStateNoConnectivity));
EXPECT_CALL(*network_, local()).WillRepeatedly(Return(ip_addr));
EXPECT_CALL(*network_, dns_servers()).WillRepeatedly(Return(dns_list));
EXPECT_CALL(*device_, StartConnectionDiagnosticsAfterPortalDetection());
MockPortalDetector* portal_detector = SetMockPortalDetector();
EXPECT_CALL(*portal_detector, GetNextAttemptDelay())
.WillRepeatedly(Return(attempt_delay));
EXPECT_CALL(*portal_detector,
Start(_, kDeviceName, ip_addr, dns_list, _, attempt_delay))
.WillRepeatedly(Return(true));
PortalDetectorCallback(result);
}
TEST_F(DevicePortalDetectionTest, PortalDetectionRedirect) {
const auto ip_addr = IPAddress("1.2.3.4");
const std::vector<std::string> dns_list = {"8.8.8.8", "8.8.4.4"};
const auto props = MakePortalProperties();
PortalDetector::Result result;
result.http_phase = PortalDetector::Phase::kContent,
result.http_status = PortalDetector::Status::kRedirect;
result.http_status_code = 302;
result.https_phase = PortalDetector::Phase::kContent;
result.https_status = PortalDetector::Status::kSuccess;
result.redirect_url_string = props.portal_http_url;
result.num_attempts = kPortalAttempts;
const auto attempt_delay = base::Milliseconds(13450);
EXPECT_CALL(manager_, GetProperties()).WillRepeatedly(ReturnRef(props));
EXPECT_CALL(*service_, IsConnected(nullptr)).WillRepeatedly(Return(true));
EXPECT_CALL(*service_,
SetPortalDetectionFailure(kPortalDetectionPhaseContent,
kPortalDetectionStatusRedirect, 302));
EXPECT_CALL(*service_, SetState(Service::kStateRedirectFound));
EXPECT_CALL(*network_, local()).WillRepeatedly(Return(ip_addr));
EXPECT_CALL(*network_, dns_servers()).WillRepeatedly(Return(dns_list));
EXPECT_CALL(*device_, StartConnectionDiagnosticsAfterPortalDetection())
.Times(0);
MockPortalDetector* portal_detector = SetMockPortalDetector();
EXPECT_CALL(*portal_detector, GetNextAttemptDelay())
.WillRepeatedly(Return(attempt_delay));
EXPECT_CALL(*portal_detector,
Start(_, kDeviceName, ip_addr, dns_list, _, attempt_delay))
.WillRepeatedly(Return(true));
PortalDetectorCallback(result);
}
TEST_F(DevicePortalDetectionTest, PortalDetectionRedirectNoUrl) {
const auto ip_addr = IPAddress("1.2.3.4");
const std::vector<std::string> dns_list = {"8.8.8.8", "8.8.4.4"};
const auto props = MakePortalProperties();
PortalDetector::Result result;
result.http_phase = PortalDetector::Phase::kContent,
result.http_status = PortalDetector::Status::kRedirect;
result.http_status_code = 302;
result.https_phase = PortalDetector::Phase::kContent;
result.https_status = PortalDetector::Status::kSuccess;
result.num_attempts = kPortalAttempts;
const auto attempt_delay = base::Milliseconds(13450);
EXPECT_CALL(manager_, GetProperties()).WillRepeatedly(ReturnRef(props));
EXPECT_CALL(*service_, IsConnected(nullptr)).WillRepeatedly(Return(true));
EXPECT_CALL(*service_,
SetPortalDetectionFailure(kPortalDetectionPhaseContent,
kPortalDetectionStatusRedirect, 302));
EXPECT_CALL(*service_, SetState(Service::kStatePortalSuspected));
EXPECT_CALL(*network_, local()).WillRepeatedly(Return(ip_addr));
EXPECT_CALL(*network_, dns_servers()).WillRepeatedly(Return(dns_list));
EXPECT_CALL(*device_, StartConnectionDiagnosticsAfterPortalDetection());
MockPortalDetector* portal_detector = SetMockPortalDetector();
EXPECT_CALL(*portal_detector, GetNextAttemptDelay())
.WillRepeatedly(Return(attempt_delay));
EXPECT_CALL(*portal_detector,
Start(_, kDeviceName, ip_addr, dns_list, _, attempt_delay))
.WillRepeatedly(Return(true));
PortalDetectorCallback(result);
}
TEST_F(DevicePortalDetectionTest, PortalDetectionPortalSuspected) {
const auto ip_addr = IPAddress("1.2.3.4");
const std::vector<std::string> dns_list = {"8.8.8.8", "8.8.4.4"};
const auto props = MakePortalProperties();
PortalDetector::Result result;
result.http_phase = PortalDetector::Phase::kContent,
result.http_status = PortalDetector::Status::kSuccess;
result.http_status_code = 204;
result.https_phase = PortalDetector::Phase::kContent;
result.https_status = PortalDetector::Status::kFailure;
result.num_attempts = kPortalAttempts;
const auto attempt_delay = base::Milliseconds(13450);
EXPECT_CALL(manager_, GetProperties()).WillRepeatedly(ReturnRef(props));
EXPECT_CALL(*service_, IsConnected(nullptr)).WillRepeatedly(Return(true));
EXPECT_CALL(*service_,
SetPortalDetectionFailure(kPortalDetectionPhaseContent,
kPortalDetectionStatusSuccess, 204));
EXPECT_CALL(*service_, SetState(Service::kStatePortalSuspected));
EXPECT_CALL(*network_, local()).WillRepeatedly(Return(ip_addr));
EXPECT_CALL(*network_, dns_servers()).WillRepeatedly(Return(dns_list));
EXPECT_CALL(*device_, StartConnectionDiagnosticsAfterPortalDetection());
MockPortalDetector* portal_detector = SetMockPortalDetector();
EXPECT_CALL(*portal_detector, GetNextAttemptDelay())
.WillRepeatedly(Return(attempt_delay));
EXPECT_CALL(*portal_detector,
Start(_, kDeviceName, ip_addr, dns_list, _, attempt_delay))
.WillRepeatedly(Return(true));
PortalDetectorCallback(result);
}
TEST_F(DevicePortalDetectionTest, PortalDetectionNoConnectivity) {
const auto ip_addr = IPAddress("1.2.3.4");
const std::vector<std::string> dns_list = {"8.8.8.8", "8.8.4.4"};
const auto props = MakePortalProperties();
PortalDetector::Result result;
result.http_phase = PortalDetector::Phase::kUnknown,
result.http_status = PortalDetector::Status::kFailure;
result.http_status_code = 0;
result.https_phase = PortalDetector::Phase::kContent;
result.https_status = PortalDetector::Status::kFailure;
result.num_attempts = kPortalAttempts;
const auto attempt_delay = base::Milliseconds(13450);
EXPECT_CALL(manager_, GetProperties()).WillRepeatedly(ReturnRef(props));
EXPECT_CALL(*service_, IsConnected(nullptr)).WillRepeatedly(Return(true));
EXPECT_CALL(*service_,
SetPortalDetectionFailure(kPortalDetectionPhaseUnknown,
kPortalDetectionStatusFailure, 0));
EXPECT_CALL(*service_, SetState(Service::kStateNoConnectivity));
EXPECT_CALL(*network_, local()).WillRepeatedly(Return(ip_addr));
EXPECT_CALL(*network_, dns_servers()).WillRepeatedly(Return(dns_list));
EXPECT_CALL(*device_, StartConnectionDiagnosticsAfterPortalDetection());
MockPortalDetector* portal_detector = SetMockPortalDetector();
EXPECT_CALL(*portal_detector, GetNextAttemptDelay())
.WillRepeatedly(Return(attempt_delay));
EXPECT_CALL(*portal_detector,
Start(_, kDeviceName, ip_addr, dns_list, _, attempt_delay))
.WillRepeatedly(Return(true));
PortalDetectorCallback(result);
}
} // namespace shill