2300 lines
87 KiB
C++
2300 lines
87 KiB
C++
//
|
|
// Copyright (C) 2013 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/service.h"
|
|
|
|
#include <algorithm>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <base/bind.h>
|
|
#if defined(__ANDROID__)
|
|
#include <dbus/service_constants.h>
|
|
#else
|
|
#include <chromeos/dbus/service_constants.h>
|
|
#endif // __ANDROID__
|
|
#include <gtest/gtest.h>
|
|
#include <gmock/gmock.h>
|
|
|
|
#include "shill/ethernet/ethernet_service.h"
|
|
#include "shill/event_dispatcher.h"
|
|
#include "shill/manager.h"
|
|
#include "shill/mock_adaptors.h"
|
|
#include "shill/mock_connection.h"
|
|
#include "shill/mock_control.h"
|
|
#include "shill/mock_device_info.h"
|
|
#include "shill/mock_dhcp_properties.h"
|
|
#include "shill/mock_event_dispatcher.h"
|
|
#include "shill/mock_log.h"
|
|
#include "shill/mock_manager.h"
|
|
#include "shill/mock_power_manager.h"
|
|
#include "shill/mock_profile.h"
|
|
#include "shill/mock_service.h"
|
|
#include "shill/mock_store.h"
|
|
#include "shill/net/mock_time.h"
|
|
#include "shill/property_store_unittest.h"
|
|
#include "shill/service_property_change_test.h"
|
|
#include "shill/service_sorter.h"
|
|
#include "shill/service_under_test.h"
|
|
#include "shill/testing.h"
|
|
|
|
#if !defined(DISABLE_WIFI) || !defined(DISABLE_WIRED_8021X)
|
|
#include "shill/mock_eap_credentials.h"
|
|
#endif // DISABLE_WIFI || DISABLE_WIRED_8021X
|
|
|
|
using base::Bind;
|
|
using base::Unretained;
|
|
using std::deque;
|
|
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::Mock;
|
|
using testing::NiceMock;
|
|
using testing::Return;
|
|
using testing::ReturnNull;
|
|
using testing::ReturnRef;
|
|
using testing::StrictMock;
|
|
using testing::SetArgumentPointee;
|
|
using testing::Test;
|
|
using testing::Values;
|
|
|
|
namespace shill {
|
|
|
|
class ServiceTest : public PropertyStoreTest {
|
|
public:
|
|
ServiceTest()
|
|
: mock_manager_(control_interface(), dispatcher(), metrics()),
|
|
service_(new ServiceUnderTest(control_interface(),
|
|
dispatcher(),
|
|
metrics(),
|
|
&mock_manager_)),
|
|
service2_(new ServiceUnderTest(control_interface(),
|
|
dispatcher(),
|
|
metrics(),
|
|
&mock_manager_)),
|
|
storage_id_(ServiceUnderTest::kStorageId),
|
|
#if !defined(DISABLE_WIFI) || !defined(DISABLE_WIRED_8021X)
|
|
eap_(new MockEapCredentials()),
|
|
#endif // DISABLE_WIFI || DISABLE_WIRED_8021X
|
|
power_manager_(new MockPowerManager(nullptr, &control_)) {
|
|
ON_CALL(control_, CreatePowerManagerProxy(_, _, _))
|
|
.WillByDefault(ReturnNull());
|
|
|
|
service_->time_ = &time_;
|
|
service_->disconnects_.time_ = &time_;
|
|
service_->misconnects_.time_ = &time_;
|
|
DefaultValue<Timestamp>::Set(Timestamp());
|
|
#if !defined(DISABLE_WIFI) || !defined(DISABLE_WIRED_8021X)
|
|
service_->eap_.reset(eap_); // Passes ownership.
|
|
#endif // DISABLE_WIFI || DISABLE_WIRED_8021X
|
|
mock_manager_.running_ = true;
|
|
mock_manager_.set_power_manager(power_manager_); // Passes ownership.
|
|
}
|
|
|
|
virtual ~ServiceTest() {}
|
|
|
|
MOCK_METHOD1(TestCallback, void(const Error& error));
|
|
|
|
protected:
|
|
typedef scoped_refptr<MockProfile> MockProfileRefPtr;
|
|
|
|
ServiceMockAdaptor* GetAdaptor() {
|
|
return static_cast<ServiceMockAdaptor*>(service_->adaptor());
|
|
}
|
|
|
|
string GetFriendlyName() { return service_->friendly_name(); }
|
|
|
|
void SetManagerRunning(bool running) { mock_manager_.running_ = running; }
|
|
|
|
void SetSuspending(bool suspending) {
|
|
power_manager_->suspending_ = suspending;
|
|
}
|
|
|
|
bool GetExplicitlyDisconnected() const {
|
|
return service_->explicitly_disconnected_;
|
|
}
|
|
|
|
void SetExplicitlyDisconnected(bool explicitly) {
|
|
service_->explicitly_disconnected_ = explicitly;
|
|
}
|
|
|
|
void SetStateField(Service::ConnectState state) { service_->state_ = state; }
|
|
|
|
Service::ConnectState GetPreviousState() const {
|
|
return service_->previous_state_;
|
|
}
|
|
|
|
void NoteDisconnectEvent() {
|
|
service_->NoteDisconnectEvent();
|
|
}
|
|
|
|
EventHistory* GetDisconnects() {
|
|
return &service_->disconnects_;
|
|
}
|
|
EventHistory* GetMisconnects() {
|
|
return &service_->misconnects_;
|
|
}
|
|
|
|
Timestamp GetTimestamp(int monotonic_seconds, int boottime_seconds,
|
|
const string& wall_clock) {
|
|
struct timeval monotonic = { .tv_sec = monotonic_seconds, .tv_usec = 0 };
|
|
struct timeval boottime = { .tv_sec = boottime_seconds, .tv_usec = 0 };
|
|
return Timestamp(monotonic, boottime, wall_clock);
|
|
}
|
|
|
|
void PushTimestamp(EventHistory* events,
|
|
int monotonic_seconds,
|
|
int boottime_seconds,
|
|
const string& wall_clock) {
|
|
events->RecordEventInternal(
|
|
GetTimestamp(monotonic_seconds, boottime_seconds, wall_clock));
|
|
}
|
|
|
|
int GetDisconnectsMonitorSeconds() {
|
|
return Service::kDisconnectsMonitorSeconds;
|
|
}
|
|
|
|
int GetMisconnectsMonitorSeconds() {
|
|
return Service::kMisconnectsMonitorSeconds;
|
|
}
|
|
|
|
int GetMaxDisconnectEventHistory() {
|
|
return Service::kMaxDisconnectEventHistory;
|
|
}
|
|
|
|
int GetMaxMisconnectEventHistory() {
|
|
return Service::kMaxMisconnectEventHistory;
|
|
}
|
|
|
|
bool GetAutoConnect(Error* error) {
|
|
return service_->GetAutoConnect(error);
|
|
}
|
|
|
|
void ClearAutoConnect(Error* error) {
|
|
service_->ClearAutoConnect(error);
|
|
}
|
|
|
|
bool SetAutoConnectFull(bool connect, Error* error) {
|
|
return service_->SetAutoConnectFull(connect, error);
|
|
}
|
|
|
|
bool SortingOrderIs(const ServiceRefPtr& service0,
|
|
const ServiceRefPtr& service1,
|
|
bool should_compare_connectivity_state) {
|
|
vector<ServiceRefPtr> services;
|
|
services.push_back(service1);
|
|
services.push_back(service0);
|
|
std::sort(services.begin(), services.end(),
|
|
ServiceSorter(&mock_manager_, should_compare_connectivity_state,
|
|
technology_order_for_sorting_));
|
|
return (service0.get() == services[0].get() &&
|
|
service1.get() == services[1].get());
|
|
}
|
|
|
|
bool DefaultSortingOrderIs(const ServiceRefPtr& service0,
|
|
const ServiceRefPtr& service1) {
|
|
const bool kShouldCompareConnectivityState = true;
|
|
return SortingOrderIs(
|
|
service0, service1, kShouldCompareConnectivityState);
|
|
}
|
|
|
|
MockManager mock_manager_;
|
|
MockTime time_;
|
|
scoped_refptr<ServiceUnderTest> service_;
|
|
scoped_refptr<ServiceUnderTest> service2_;
|
|
string storage_id_;
|
|
NiceMock<MockControl> control_;
|
|
#if !defined(DISABLE_WIFI) || !defined(DISABLE_WIRED_8021X)
|
|
MockEapCredentials* eap_; // Owned by |service_|.
|
|
#endif // DISABLE_WIFI || DISABLE_WIRED_8021X
|
|
MockPowerManager* power_manager_; // Owned by |mock_manager_|.
|
|
vector<Technology::Identifier> technology_order_for_sorting_;
|
|
};
|
|
|
|
class AllMockServiceTest : public testing::Test {
|
|
public:
|
|
AllMockServiceTest()
|
|
: metrics_(&dispatcher_),
|
|
manager_(&control_interface_, &dispatcher_, &metrics_),
|
|
service_(new ServiceUnderTest(&control_interface_,
|
|
&dispatcher_,
|
|
&metrics_,
|
|
&manager_)) { }
|
|
virtual ~AllMockServiceTest() {}
|
|
|
|
protected:
|
|
MockControl control_interface_;
|
|
StrictMock<MockEventDispatcher> dispatcher_;
|
|
NiceMock<MockMetrics> metrics_;
|
|
MockManager manager_;
|
|
scoped_refptr<ServiceUnderTest> service_;
|
|
};
|
|
|
|
TEST_F(ServiceTest, Constructor) {
|
|
EXPECT_TRUE(service_->save_credentials_);
|
|
EXPECT_EQ(Service::kCheckPortalAuto, service_->check_portal_);
|
|
EXPECT_EQ(Service::kStateIdle, service_->state());
|
|
EXPECT_FALSE(service_->has_ever_connected());
|
|
EXPECT_EQ(0, service_->previous_error_serial_number_);
|
|
EXPECT_EQ("", service_->previous_error_);
|
|
}
|
|
|
|
TEST_F(ServiceTest, CalculateState) {
|
|
service_->state_ = Service::kStateConnected;
|
|
Error error;
|
|
EXPECT_EQ(kStateReady, service_->CalculateState(&error));
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
}
|
|
|
|
TEST_F(ServiceTest, CalculateTechnology) {
|
|
service_->technology_ = Technology::kWifi;
|
|
Error error;
|
|
EXPECT_EQ(kTypeWifi, service_->CalculateTechnology(&error));
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
}
|
|
|
|
TEST_F(ServiceTest, GetProperties) {
|
|
{
|
|
brillo::VariantDictionary props;
|
|
Error error;
|
|
string expected("true");
|
|
service_->mutable_store()->SetStringProperty(kCheckPortalProperty,
|
|
expected,
|
|
&error);
|
|
EXPECT_TRUE(service_->store().GetProperties(&props, &error));
|
|
ASSERT_FALSE(props.find(kCheckPortalProperty) == props.end());
|
|
EXPECT_TRUE(props[kCheckPortalProperty].IsTypeCompatible<string>());
|
|
EXPECT_EQ(props[kCheckPortalProperty].Get<string>(), expected);
|
|
}
|
|
{
|
|
brillo::VariantDictionary props;
|
|
Error error;
|
|
bool expected = true;
|
|
service_->mutable_store()->SetBoolProperty(kAutoConnectProperty,
|
|
expected,
|
|
&error);
|
|
EXPECT_TRUE(service_->store().GetProperties(&props, &error));
|
|
ASSERT_FALSE(props.find(kAutoConnectProperty) == props.end());
|
|
EXPECT_TRUE(props[kAutoConnectProperty].IsTypeCompatible<bool>());
|
|
EXPECT_EQ(props[kAutoConnectProperty].Get<bool>(), expected);
|
|
}
|
|
{
|
|
brillo::VariantDictionary props;
|
|
Error error;
|
|
EXPECT_TRUE(service_->store().GetProperties(&props, &error));
|
|
ASSERT_FALSE(props.find(kConnectableProperty) == props.end());
|
|
EXPECT_TRUE(props[kConnectableProperty].IsTypeCompatible<bool>());
|
|
EXPECT_EQ(props[kConnectableProperty].Get<bool>(), false);
|
|
}
|
|
{
|
|
brillo::VariantDictionary props;
|
|
Error error;
|
|
int32_t expected = 127;
|
|
service_->mutable_store()->SetInt32Property(kPriorityProperty,
|
|
expected,
|
|
&error);
|
|
EXPECT_TRUE(service_->store().GetProperties(&props, &error));
|
|
ASSERT_FALSE(props.find(kPriorityProperty) == props.end());
|
|
EXPECT_TRUE(props[kPriorityProperty].IsTypeCompatible<int32_t>());
|
|
EXPECT_EQ(props[kPriorityProperty].Get<int32_t>(), expected);
|
|
}
|
|
{
|
|
brillo::VariantDictionary props;
|
|
Error error;
|
|
service_->store().GetProperties(&props, &error);
|
|
ASSERT_FALSE(props.find(kDeviceProperty) == props.end());
|
|
EXPECT_TRUE(props[kDeviceProperty].IsTypeCompatible<dbus::ObjectPath>());
|
|
EXPECT_EQ(props[kDeviceProperty].Get<dbus::ObjectPath>().value(),
|
|
string(ServiceUnderTest::kRpcId));
|
|
}
|
|
}
|
|
|
|
TEST_F(ServiceTest, SetProperty) {
|
|
{
|
|
Error error;
|
|
EXPECT_TRUE(service_->mutable_store()->SetAnyProperty(
|
|
kSaveCredentialsProperty, PropertyStoreTest::kBoolV, &error));
|
|
}
|
|
{
|
|
Error error;
|
|
const int32_t priority = 1;
|
|
EXPECT_TRUE(service_->mutable_store()->SetAnyProperty(
|
|
kPriorityProperty, brillo::Any(priority), &error));
|
|
}
|
|
{
|
|
Error error;
|
|
const string guid("not default");
|
|
EXPECT_TRUE(service_->mutable_store()->SetAnyProperty(
|
|
kGuidProperty, brillo::Any(guid), &error));
|
|
}
|
|
#if !defined(DISABLE_WIFI) || !defined(DISABLE_WIRED_8021X)
|
|
// Ensure that EAP properties cannot be set on services with no EAP
|
|
// credentials. Use service2_ here since we're have some code in
|
|
// ServiceTest::SetUp() that fiddles with service_->eap_.
|
|
{
|
|
Error error;
|
|
string eap("eap eep eip!");
|
|
EXPECT_FALSE(service2_->mutable_store()->SetAnyProperty(
|
|
kEapMethodProperty, brillo::Any(eap), &error));
|
|
ASSERT_TRUE(error.IsFailure());
|
|
EXPECT_EQ(Error::kInvalidProperty, error.type());
|
|
// Now plumb in eap credentials, and try again.
|
|
service2_->SetEapCredentials(new EapCredentials());
|
|
EXPECT_TRUE(service2_->mutable_store()->SetAnyProperty(
|
|
kEapMethodProperty, brillo::Any(eap), &error));
|
|
}
|
|
#endif // DISABLE_WIFI || DISABLE_WIRED_8021X
|
|
// Ensure that an attempt to write a R/O property returns InvalidArgs error.
|
|
{
|
|
Error error;
|
|
EXPECT_FALSE(service_->mutable_store()->SetAnyProperty(
|
|
kConnectableProperty, PropertyStoreTest::kBoolV, &error));
|
|
ASSERT_TRUE(error.IsFailure());
|
|
EXPECT_EQ(Error::kInvalidArguments, error.type());
|
|
}
|
|
{
|
|
bool auto_connect = true;
|
|
Error error;
|
|
EXPECT_TRUE(service_->mutable_store()->SetAnyProperty(
|
|
kAutoConnectProperty, brillo::Any(auto_connect), &error));
|
|
}
|
|
// Ensure that we can perform a trivial set of the Name property (to its
|
|
// current value) but an attempt to set the property to a different value
|
|
// fails.
|
|
{
|
|
Error error;
|
|
EXPECT_FALSE(service_->mutable_store()->SetAnyProperty(
|
|
kNameProperty, brillo::Any(GetFriendlyName()), &error));
|
|
EXPECT_FALSE(error.IsFailure());
|
|
}
|
|
{
|
|
Error error;
|
|
EXPECT_FALSE(service_->mutable_store()->SetAnyProperty(
|
|
kNameProperty, PropertyStoreTest::kStringV, &error));
|
|
ASSERT_TRUE(error.IsFailure());
|
|
EXPECT_EQ(Error::kInvalidArguments, error.type());
|
|
}
|
|
}
|
|
|
|
TEST_F(ServiceTest, GetLoadableStorageIdentifier) {
|
|
NiceMock<MockStore> storage;
|
|
EXPECT_CALL(storage, ContainsGroup(storage_id_))
|
|
.WillOnce(Return(false))
|
|
.WillOnce(Return(true));
|
|
EXPECT_EQ("", service_->GetLoadableStorageIdentifier(storage));
|
|
EXPECT_EQ(storage_id_, service_->GetLoadableStorageIdentifier(storage));
|
|
}
|
|
|
|
TEST_F(ServiceTest, IsLoadableFrom) {
|
|
NiceMock<MockStore> storage;
|
|
EXPECT_CALL(storage, ContainsGroup(storage_id_))
|
|
.WillOnce(Return(false))
|
|
.WillOnce(Return(true));
|
|
EXPECT_FALSE(service_->IsLoadableFrom(storage));
|
|
EXPECT_TRUE(service_->IsLoadableFrom(storage));
|
|
}
|
|
|
|
#if !defined(DISABLE_WIFI) || !defined(DISABLE_WIRED_8021X)
|
|
class ServiceWithOnEapCredentialsChangedOverride : public ServiceUnderTest {
|
|
public:
|
|
ServiceWithOnEapCredentialsChangedOverride(
|
|
ControlInterface* control_interface,
|
|
EventDispatcher* dispatcher,
|
|
Metrics* metrics,
|
|
Manager* manager,
|
|
EapCredentials* eap)
|
|
: ServiceUnderTest(control_interface, dispatcher, metrics, manager) {
|
|
SetEapCredentials(eap);
|
|
}
|
|
void OnEapCredentialsChanged(Service::UpdateCredentialsReason) override {
|
|
SetHasEverConnected(false);
|
|
}
|
|
};
|
|
#endif // DISABLE_WIFI || DISABLE_WIRED_8021X
|
|
|
|
TEST_F(ServiceTest, Load) {
|
|
#if !defined(DISABLE_WIFI) || !defined(DISABLE_WIRED_8021X)
|
|
MockEapCredentials* eap = new MockEapCredentials(); // Owned by |service|.
|
|
scoped_refptr<ServiceWithOnEapCredentialsChangedOverride> service(
|
|
new ServiceWithOnEapCredentialsChangedOverride(control_interface(),
|
|
dispatcher(),
|
|
metrics(),
|
|
&mock_manager_,
|
|
eap));
|
|
#else
|
|
scoped_refptr<ServiceUnderTest> service(
|
|
new ServiceUnderTest(control_interface(),
|
|
dispatcher(),
|
|
metrics(),
|
|
&mock_manager_));
|
|
#endif // DISABLE_WIFI || DISABLE_WIRED_8021X
|
|
|
|
NiceMock<MockStore> storage;
|
|
EXPECT_CALL(storage, ContainsGroup(storage_id_)).WillOnce(Return(true));
|
|
const string kCheckPortal("check-portal");
|
|
const string kGUID("guid");
|
|
const bool kHasEverConnected = true;
|
|
const int kPriority = 20;
|
|
const string kProxyConfig("proxy-config");
|
|
const string kUIData("ui-data");
|
|
EXPECT_CALL(storage, GetString(storage_id_, _, _)).Times(AnyNumber());
|
|
EXPECT_CALL(storage, GetInt(storage_id_, _, _)).Times(AnyNumber());
|
|
EXPECT_CALL(storage, GetString(storage_id_, Service::kStorageCheckPortal, _))
|
|
.WillRepeatedly(DoAll(SetArgumentPointee<2>(kCheckPortal), Return(true)));
|
|
EXPECT_CALL(storage, GetString(storage_id_, Service::kStorageGUID, _))
|
|
.WillRepeatedly(DoAll(SetArgumentPointee<2>(kGUID), Return(true)));
|
|
EXPECT_CALL(storage, GetInt(storage_id_, Service::kStoragePriority, _))
|
|
.WillRepeatedly(DoAll(SetArgumentPointee<2>(kPriority), Return(true)));
|
|
EXPECT_CALL(storage, GetString(storage_id_, Service::kStorageProxyConfig, _))
|
|
.WillRepeatedly(DoAll(SetArgumentPointee<2>(kProxyConfig), Return(true)));
|
|
EXPECT_CALL(storage, GetString(storage_id_, Service::kStorageUIData, _))
|
|
.WillRepeatedly(DoAll(SetArgumentPointee<2>(kUIData), Return(true)));
|
|
EXPECT_CALL(storage, GetBool(storage_id_, _, _)).Times(AnyNumber());
|
|
EXPECT_CALL(storage,
|
|
GetBool(storage_id_, Service::kStorageSaveCredentials, _));
|
|
EXPECT_CALL(storage,
|
|
GetBool(storage_id_, Service::kStorageHasEverConnected, _))
|
|
.WillRepeatedly(DoAll(SetArgumentPointee<2>(kHasEverConnected),
|
|
Return(true)));
|
|
#if !defined(DISABLE_WIFI) || !defined(DISABLE_WIRED_8021X)
|
|
EXPECT_CALL(*eap, Load(&storage, storage_id_));
|
|
#endif // DISABLE_WIFI || DISABLE_WIRED_8021X
|
|
MockDhcpProperties* dhcp_props = new MockDhcpProperties();
|
|
service->dhcp_properties_.reset(dhcp_props);
|
|
EXPECT_CALL(*dhcp_props, Load(&storage, storage_id_));
|
|
|
|
EXPECT_TRUE(service->Load(&storage));
|
|
EXPECT_EQ(kCheckPortal, service->check_portal_);
|
|
EXPECT_EQ(kGUID, service->guid_);
|
|
EXPECT_TRUE(service->has_ever_connected_);
|
|
EXPECT_EQ(kProxyConfig, service->proxy_config_);
|
|
EXPECT_EQ(kUIData, service->ui_data_);
|
|
|
|
Mock::VerifyAndClearExpectations(&storage);
|
|
#if !defined(DISABLE_WIFI) || !defined(DISABLE_WIRED_8021X)
|
|
Mock::VerifyAndClearExpectations(eap_);
|
|
#endif // DISABLE_WIFI || DISABLE_WIRED_8021X
|
|
Mock::VerifyAndClearExpectations(dhcp_props);
|
|
|
|
// Assure that parameters are set to default if not available in the profile.
|
|
EXPECT_CALL(storage, ContainsGroup(storage_id_)).WillOnce(Return(true));
|
|
EXPECT_CALL(storage, GetBool(storage_id_, _, _))
|
|
.WillRepeatedly(Return(false));
|
|
EXPECT_CALL(storage, GetString(storage_id_, _, _))
|
|
.WillRepeatedly(Return(false));
|
|
EXPECT_CALL(storage, GetInt(storage_id_, _, _))
|
|
.WillRepeatedly(Return(false));
|
|
#if !defined(DISABLE_WIFI) || !defined(DISABLE_WIRED_8021X)
|
|
EXPECT_CALL(*eap, Load(&storage, storage_id_));
|
|
#endif // DISABLE_WIFI || DISABLE_WIRED_8021X
|
|
EXPECT_CALL(*dhcp_props, Load(&storage, storage_id_));
|
|
|
|
EXPECT_TRUE(service->Load(&storage));
|
|
EXPECT_EQ(Service::kCheckPortalAuto, service_->check_portal_);
|
|
EXPECT_EQ("", service->guid_);
|
|
EXPECT_EQ("", service->proxy_config_);
|
|
EXPECT_EQ("", service->ui_data_);
|
|
|
|
// has_ever_connected_ flag will reset when EAP credential changes.
|
|
#if !defined(DISABLE_WIFI) || !defined(DISABLE_WIRED_8021X)
|
|
EXPECT_FALSE(service->has_ever_connected_);
|
|
#else
|
|
EXPECT_TRUE(service->has_ever_connected_);
|
|
#endif // DISABLE_WIFI || DISABLE_WIRED_8021X
|
|
}
|
|
|
|
TEST_F(ServiceTest, LoadFail) {
|
|
StrictMock<MockStore> storage;
|
|
EXPECT_CALL(storage, ContainsGroup(storage_id_)).WillOnce(Return(false));
|
|
EXPECT_FALSE(service_->Load(&storage));
|
|
}
|
|
|
|
TEST_F(ServiceTest, LoadAutoConnect) {
|
|
NiceMock<MockStore> storage;
|
|
EXPECT_CALL(storage, ContainsGroup(storage_id_))
|
|
.WillRepeatedly(Return(true));
|
|
EXPECT_CALL(storage, GetBool(storage_id_, _, _))
|
|
.WillRepeatedly(Return(false));
|
|
EXPECT_CALL(storage, GetString(storage_id_, _, _))
|
|
.WillRepeatedly(Return(false));
|
|
EXPECT_CALL(storage, GetInt(storage_id_, _, _))
|
|
.WillRepeatedly(Return(false));
|
|
#if !defined(DISABLE_WIFI) || !defined(DISABLE_WIRED_8021X)
|
|
EXPECT_CALL(*eap_, Load(&storage, storage_id_)).Times(AnyNumber());
|
|
#endif // DISABLE_WIFI || DISABLE_WIRED_8021X
|
|
|
|
std::unique_ptr<MockDhcpProperties> dhcp_props(new MockDhcpProperties());
|
|
EXPECT_CALL(*dhcp_props.get(), Load(&storage, storage_id_)).Times(AnyNumber());
|
|
service_->dhcp_properties_ = std::move(dhcp_props);
|
|
|
|
// Three of each expectation so we can test Favorite == unset, false, true.
|
|
EXPECT_CALL(storage, GetBool(storage_id_, Service::kStorageAutoConnect, _))
|
|
.WillOnce(Return(false))
|
|
.WillOnce(Return(false))
|
|
.WillOnce(Return(false))
|
|
.WillOnce(DoAll(SetArgumentPointee<2>(false), Return(true)))
|
|
.WillOnce(DoAll(SetArgumentPointee<2>(false), Return(true)))
|
|
.WillOnce(DoAll(SetArgumentPointee<2>(false), Return(true)))
|
|
.WillOnce(DoAll(SetArgumentPointee<2>(true), Return(true)))
|
|
.WillOnce(DoAll(SetArgumentPointee<2>(true), Return(true)))
|
|
.WillOnce(DoAll(SetArgumentPointee<2>(true), Return(true)));
|
|
EXPECT_CALL(storage, GetBool(storage_id_, Service::kStorageFavorite, _))
|
|
.WillOnce(Return(false))
|
|
.WillOnce(DoAll(SetArgumentPointee<2>(false), Return(true)))
|
|
.WillOnce(DoAll(SetArgumentPointee<2>(true), Return(true)))
|
|
.WillOnce(Return(false))
|
|
.WillOnce(DoAll(SetArgumentPointee<2>(false), Return(true)))
|
|
.WillOnce(DoAll(SetArgumentPointee<2>(true), Return(true)))
|
|
.WillOnce(Return(false))
|
|
.WillOnce(DoAll(SetArgumentPointee<2>(false), Return(true)))
|
|
.WillOnce(DoAll(SetArgumentPointee<2>(true), Return(true)));
|
|
|
|
// AutoConnect is unset, Favorite is unset.
|
|
EXPECT_TRUE(service_->Load(&storage));
|
|
EXPECT_FALSE(service_->auto_connect());
|
|
EXPECT_FALSE(service_->retain_auto_connect());
|
|
|
|
// AutoConnect is unset, Favorite is false.
|
|
EXPECT_TRUE(service_->Load(&storage));
|
|
EXPECT_FALSE(service_->auto_connect());
|
|
EXPECT_FALSE(service_->retain_auto_connect());
|
|
|
|
// AutoConnect is unset, Favorite is true.
|
|
EXPECT_TRUE(service_->Load(&storage));
|
|
EXPECT_FALSE(service_->auto_connect());
|
|
EXPECT_TRUE(service_->retain_auto_connect());
|
|
|
|
// AutoConnect is false, Favorite is unset.
|
|
EXPECT_TRUE(service_->Load(&storage));
|
|
EXPECT_FALSE(service_->auto_connect());
|
|
EXPECT_TRUE(service_->retain_auto_connect());
|
|
|
|
// AutoConnect is false, Favorite is false.
|
|
EXPECT_TRUE(service_->Load(&storage));
|
|
EXPECT_FALSE(service_->auto_connect());
|
|
EXPECT_FALSE(service_->retain_auto_connect());
|
|
|
|
// AutoConnect is false, Favorite is true.
|
|
EXPECT_TRUE(service_->Load(&storage));
|
|
EXPECT_FALSE(service_->auto_connect());
|
|
EXPECT_TRUE(service_->retain_auto_connect());
|
|
|
|
// AutoConnect is true, Favorite is unset.
|
|
EXPECT_TRUE(service_->Load(&storage));
|
|
EXPECT_TRUE(service_->auto_connect());
|
|
EXPECT_TRUE(service_->retain_auto_connect());
|
|
|
|
// AutoConnect is true, Favorite is false (invalid case).
|
|
EXPECT_TRUE(service_->Load(&storage));
|
|
EXPECT_TRUE(service_->auto_connect());
|
|
EXPECT_FALSE(service_->retain_auto_connect());
|
|
|
|
// AutoConnect is true, Favorite is true.
|
|
EXPECT_TRUE(service_->Load(&storage));
|
|
EXPECT_TRUE(service_->auto_connect());
|
|
EXPECT_TRUE(service_->retain_auto_connect());
|
|
}
|
|
|
|
TEST_F(ServiceTest, SaveString) {
|
|
MockStore storage;
|
|
static const char kKey[] = "test-key";
|
|
static const char kData[] = "test-data";
|
|
EXPECT_CALL(storage, SetString(storage_id_, kKey, kData))
|
|
.WillOnce(Return(true));
|
|
service_->SaveString(&storage, storage_id_, kKey, kData, false, true);
|
|
}
|
|
|
|
TEST_F(ServiceTest, SaveStringCrypted) {
|
|
MockStore storage;
|
|
static const char kKey[] = "test-key";
|
|
static const char kData[] = "test-data";
|
|
EXPECT_CALL(storage, SetCryptedString(storage_id_, kKey, kData))
|
|
.WillOnce(Return(true));
|
|
service_->SaveString(&storage, storage_id_, kKey, kData, true, true);
|
|
}
|
|
|
|
TEST_F(ServiceTest, SaveStringDontSave) {
|
|
MockStore storage;
|
|
static const char kKey[] = "test-key";
|
|
EXPECT_CALL(storage, DeleteKey(storage_id_, kKey))
|
|
.WillOnce(Return(true));
|
|
service_->SaveString(&storage, storage_id_, kKey, "data", false, false);
|
|
}
|
|
|
|
TEST_F(ServiceTest, SaveStringEmpty) {
|
|
MockStore storage;
|
|
static const char kKey[] = "test-key";
|
|
EXPECT_CALL(storage, DeleteKey(storage_id_, kKey))
|
|
.WillOnce(Return(true));
|
|
service_->SaveString(&storage, storage_id_, kKey, "", true, true);
|
|
}
|
|
|
|
TEST_F(ServiceTest, Save) {
|
|
NiceMock<MockStore> storage;
|
|
EXPECT_CALL(storage, SetString(storage_id_, _, _))
|
|
.Times(AtLeast(1))
|
|
.WillRepeatedly(Return(true));
|
|
EXPECT_CALL(storage, DeleteKey(storage_id_, _))
|
|
.Times(AtLeast(1))
|
|
.WillRepeatedly(Return(true));
|
|
EXPECT_CALL(storage, DeleteKey(storage_id_, Service::kStorageFavorite))
|
|
.WillOnce(Return(true));
|
|
EXPECT_CALL(storage, DeleteKey(storage_id_, Service::kStorageAutoConnect))
|
|
.WillOnce(Return(true));
|
|
EXPECT_CALL(storage, SetBool(storage_id_, _, _)).Times(AnyNumber());
|
|
EXPECT_CALL(storage,
|
|
SetBool(storage_id_,
|
|
Service::kStorageSaveCredentials,
|
|
service_->save_credentials()));
|
|
#if !defined(DISABLE_WIFI) || !defined(DISABLE_WIRED_8021X)
|
|
EXPECT_CALL(*eap_, Save(&storage, storage_id_, true));
|
|
#endif // DISABLE_WIFI || DISABLE_WIRED_8021X
|
|
std::unique_ptr<MockDhcpProperties> dhcp_props(new MockDhcpProperties());
|
|
EXPECT_CALL(*dhcp_props.get(), Save(&storage, storage_id_));
|
|
service_->dhcp_properties_ = std::move(dhcp_props);
|
|
EXPECT_TRUE(service_->Save(&storage));
|
|
}
|
|
|
|
TEST_F(ServiceTest, RetainAutoConnect) {
|
|
NiceMock<MockStore> storage;
|
|
EXPECT_CALL(storage, SetString(storage_id_, _, _))
|
|
.Times(AtLeast(1))
|
|
.WillRepeatedly(Return(true));
|
|
EXPECT_CALL(storage, DeleteKey(storage_id_, _))
|
|
.Times(AtLeast(1))
|
|
.WillRepeatedly(Return(true));
|
|
EXPECT_CALL(storage, DeleteKey(storage_id_, Service::kStorageFavorite))
|
|
.Times(2);
|
|
EXPECT_CALL(storage, DeleteKey(storage_id_, Service::kStorageAutoConnect))
|
|
.Times(0);
|
|
EXPECT_CALL(storage, SetBool(storage_id_, _, _)).Times(AnyNumber());
|
|
#if !defined(DISABLE_WIFI) || !defined(DISABLE_WIRED_8021X)
|
|
EXPECT_CALL(*eap_, Save(&storage, storage_id_, true)).Times(2);
|
|
#endif // DISABLE_WIFI || DISABLE_WIRED_8021X
|
|
|
|
// AutoConnect flag set true.
|
|
service_->EnableAndRetainAutoConnect();
|
|
EXPECT_CALL(storage,
|
|
SetBool(storage_id_, Service::kStorageAutoConnect, true));
|
|
EXPECT_TRUE(service_->Save(&storage));
|
|
|
|
// AutoConnect flag set false.
|
|
EXPECT_CALL(storage,
|
|
SetBool(storage_id_, Service::kStorageAutoConnect, false));
|
|
service_->SetAutoConnect(false);
|
|
EXPECT_TRUE(service_->Save(&storage));
|
|
}
|
|
|
|
TEST_F(ServiceTest, HasEverConnectedSavedToProfile) {
|
|
NiceMock<MockStore> storage;
|
|
EXPECT_CALL(storage, SetString(storage_id_, _, _))
|
|
.Times(AtLeast(1))
|
|
.WillRepeatedly(Return(true));
|
|
EXPECT_CALL(storage, DeleteKey(storage_id_, _))
|
|
.Times(AtLeast(1))
|
|
.WillRepeatedly(Return(true));
|
|
EXPECT_CALL(storage,
|
|
DeleteKey(storage_id_, Service::kStorageHasEverConnected))
|
|
.Times(0);
|
|
EXPECT_CALL(storage, SetBool(storage_id_, _, _)).Times(AnyNumber());
|
|
#if !defined(DISABLE_WIFI) || !defined(DISABLE_WIRED_8021X)
|
|
EXPECT_CALL(*eap_, Save(&storage, storage_id_, true)).Times(2);
|
|
#endif // DISABLE_WIFI || DISABLE_WIRED_8021X
|
|
|
|
// HasEverConnected flag set true.
|
|
service_->SetHasEverConnected(true);
|
|
EXPECT_CALL(storage,
|
|
SetBool(storage_id_, Service::kStorageHasEverConnected, true));
|
|
EXPECT_TRUE(service_->Save(&storage));
|
|
|
|
// HasEverConnected flag set false.
|
|
EXPECT_CALL(storage,
|
|
SetBool(storage_id_, Service::kStorageHasEverConnected, false));
|
|
service_->SetHasEverConnected(false);
|
|
EXPECT_TRUE(service_->Save(&storage));
|
|
}
|
|
|
|
TEST_F(ServiceTest, Unload) {
|
|
NiceMock<MockStore> storage;
|
|
EXPECT_CALL(storage, ContainsGroup(storage_id_)).WillOnce(Return(true));
|
|
static const string string_value("value");
|
|
EXPECT_CALL(storage, GetString(storage_id_, _, _))
|
|
.Times(AtLeast(1))
|
|
.WillRepeatedly(DoAll(SetArgumentPointee<2>(string_value), Return(true)));
|
|
EXPECT_CALL(storage, GetBool(storage_id_, _, _))
|
|
.Times(AtLeast(1))
|
|
.WillRepeatedly(DoAll(SetArgumentPointee<2>(true), Return(true)));
|
|
EXPECT_FALSE(service_->explicitly_disconnected_);
|
|
service_->explicitly_disconnected_ = true;
|
|
EXPECT_FALSE(service_->has_ever_connected_);
|
|
#if !defined(DISABLE_WIFI) || !defined(DISABLE_WIRED_8021X)
|
|
EXPECT_CALL(*eap_, Load(&storage, storage_id_));
|
|
#endif // DISABLE_WIFI || DISABLE_WIRED_8021X
|
|
ASSERT_TRUE(service_->Load(&storage));
|
|
// TODO(pstew): Only two string properties in the service are tested as
|
|
// a sentinel that properties are being set and reset at the right times.
|
|
// However, since property load/store is essentially a manual process,
|
|
// it is error prone and should either be exhaustively unit-tested or
|
|
// a generic framework for registering loaded/stored properties should
|
|
// be created. crbug.com/207798
|
|
EXPECT_EQ(string_value, service_->ui_data_);
|
|
EXPECT_EQ(string_value, service_->guid_);
|
|
EXPECT_FALSE(service_->explicitly_disconnected_);
|
|
EXPECT_TRUE(service_->has_ever_connected_);
|
|
service_->explicitly_disconnected_ = true;
|
|
#if !defined(DISABLE_WIFI) || !defined(DISABLE_WIRED_8021X)
|
|
EXPECT_CALL(*eap_, Reset());
|
|
#endif // DISABLE_WIFI || DISABLE_WIRED_8021X
|
|
service_->Unload();
|
|
EXPECT_EQ(string(""), service_->ui_data_);
|
|
EXPECT_EQ(string(""), service_->guid_);
|
|
EXPECT_FALSE(service_->explicitly_disconnected_);
|
|
EXPECT_FALSE(service_->has_ever_connected_);
|
|
}
|
|
|
|
TEST_F(ServiceTest, State) {
|
|
EXPECT_EQ(Service::kStateIdle, service_->state());
|
|
EXPECT_EQ(Service::kStateIdle, GetPreviousState());
|
|
EXPECT_EQ(Service::kFailureUnknown, service_->failure());
|
|
const string unknown_error(
|
|
Service::ConnectFailureToString(Service::kFailureUnknown));
|
|
EXPECT_EQ(unknown_error, service_->error());
|
|
|
|
EXPECT_CALL(*GetAdaptor(),
|
|
EmitStringChanged(kStateProperty, _)).Times(6);
|
|
EXPECT_CALL(*GetAdaptor(),
|
|
EmitStringChanged(kErrorProperty, _)).Times(4);
|
|
EXPECT_CALL(mock_manager_, UpdateService(IsRefPtrTo(service_)));
|
|
service_->SetState(Service::kStateConnected);
|
|
EXPECT_EQ(Service::kStateIdle, GetPreviousState());
|
|
// A second state change shouldn't cause another update
|
|
service_->SetState(Service::kStateConnected);
|
|
EXPECT_EQ(Service::kStateConnected, service_->state());
|
|
EXPECT_EQ(Service::kStateIdle, GetPreviousState());
|
|
EXPECT_EQ(Service::kFailureUnknown, service_->failure());
|
|
EXPECT_TRUE(service_->has_ever_connected_);
|
|
|
|
EXPECT_CALL(mock_manager_, UpdateService(IsRefPtrTo(service_)));
|
|
service_->SetFailure(Service::kFailureOutOfRange);
|
|
EXPECT_TRUE(service_->IsFailed());
|
|
EXPECT_GT(service_->failed_time_, 0);
|
|
EXPECT_GT(service_->previous_error_serial_number_, 0);
|
|
EXPECT_EQ(Service::kStateFailure, service_->state());
|
|
EXPECT_EQ(Service::kFailureOutOfRange, service_->failure());
|
|
const string out_of_range_error(
|
|
Service::ConnectFailureToString(Service::kFailureOutOfRange));
|
|
EXPECT_EQ(out_of_range_error, service_->error());
|
|
EXPECT_EQ(out_of_range_error, service_->previous_error_);
|
|
|
|
EXPECT_CALL(mock_manager_, UpdateService(IsRefPtrTo(service_)));
|
|
service_->SetState(Service::kStateConnected);
|
|
EXPECT_FALSE(service_->IsFailed());
|
|
EXPECT_EQ(service_->failed_time_, 0);
|
|
EXPECT_EQ(unknown_error, service_->error());
|
|
EXPECT_EQ(out_of_range_error, service_->previous_error_);
|
|
EXPECT_GT(service_->previous_error_serial_number_, 0);
|
|
|
|
EXPECT_CALL(mock_manager_, UpdateService(IsRefPtrTo(service_)));
|
|
service_->SetFailureSilent(Service::kFailurePinMissing);
|
|
EXPECT_TRUE(service_->IsFailed());
|
|
EXPECT_GT(service_->failed_time_, 0);
|
|
EXPECT_GT(service_->previous_error_serial_number_, 0);
|
|
EXPECT_EQ(Service::kStateIdle, service_->state());
|
|
EXPECT_EQ(Service::kFailurePinMissing, service_->failure());
|
|
const string pin_missing_error(
|
|
Service::ConnectFailureToString(Service::kFailurePinMissing));
|
|
EXPECT_EQ(pin_missing_error, service_->error());
|
|
EXPECT_EQ(pin_missing_error, service_->previous_error_);
|
|
|
|
// If the Service has a Profile, the profile should be saved when
|
|
// the service enters kStateConnected. (The case where the service
|
|
// doesn't have a profile is tested above.)
|
|
MockProfileRefPtr mock_profile(
|
|
new MockProfile(control_interface(), metrics(), &mock_manager_));
|
|
NiceMock<MockStore> storage;
|
|
service_->set_profile(mock_profile);
|
|
service_->has_ever_connected_ = false;
|
|
EXPECT_CALL(mock_manager_, UpdateService(IsRefPtrTo(service_)));
|
|
EXPECT_CALL(*mock_profile, GetConstStorage())
|
|
.WillOnce(Return(&storage));
|
|
EXPECT_CALL(*mock_profile, UpdateService(IsRefPtrTo(service_)));
|
|
service_->SetState(Service::kStateConnected);
|
|
EXPECT_TRUE(service_->has_ever_connected_);
|
|
service_->set_profile(nullptr); // Break reference cycle.
|
|
|
|
// Similar to the above, but emulate an emphemeral profile, which
|
|
// has no storage. We can't update the service in the profile, but
|
|
// we should not crash.
|
|
service_->state_ = Service::kStateIdle; // Skips state change logic.
|
|
service_->set_profile(mock_profile);
|
|
service_->has_ever_connected_ = false;
|
|
EXPECT_CALL(mock_manager_, UpdateService(IsRefPtrTo(service_)));
|
|
EXPECT_CALL(*mock_profile, GetConstStorage()).WillOnce(Return(nullptr));
|
|
service_->SetState(Service::kStateConnected);
|
|
EXPECT_TRUE(service_->has_ever_connected_);
|
|
service_->set_profile(nullptr); // Break reference cycle.
|
|
}
|
|
|
|
TEST_F(ServiceTest, PortalDetectionFailure) {
|
|
EXPECT_CALL(*GetAdaptor(),
|
|
EmitStringChanged(kPortalDetectionFailedPhaseProperty,
|
|
kPortalDetectionPhaseDns)).Times(1);
|
|
EXPECT_CALL(*GetAdaptor(),
|
|
EmitStringChanged(kPortalDetectionFailedStatusProperty,
|
|
kPortalDetectionStatusTimeout)).Times(1);
|
|
service_->SetPortalDetectionFailure(kPortalDetectionPhaseDns,
|
|
kPortalDetectionStatusTimeout);
|
|
EXPECT_EQ(kPortalDetectionPhaseDns,
|
|
service_->portal_detection_failure_phase_);
|
|
EXPECT_EQ(kPortalDetectionStatusTimeout,
|
|
service_->portal_detection_failure_status_);
|
|
}
|
|
|
|
TEST_F(ServiceTest, StateResetAfterFailure) {
|
|
service_->SetFailure(Service::kFailureOutOfRange);
|
|
EXPECT_EQ(Service::kStateFailure, service_->state());
|
|
Error error;
|
|
service_->Connect(&error, "in test");
|
|
EXPECT_EQ(Service::kStateIdle, service_->state());
|
|
EXPECT_EQ(Service::kFailureUnknown, service_->failure());
|
|
|
|
service_->SetState(Service::kStateConnected);
|
|
service_->Connect(&error, "in test");
|
|
EXPECT_EQ(Service::kStateConnected, service_->state());
|
|
}
|
|
|
|
TEST_F(ServiceTest, UserInitiatedConnectionResult) {
|
|
service_->technology_ = Technology::kWifi;
|
|
Error error;
|
|
// User-initiated connection attempt succeed.
|
|
service_->SetState(Service::kStateIdle);
|
|
service_->UserInitiatedConnect(&error);
|
|
EXPECT_CALL(*metrics(), NotifyUserInitiatedConnectionResult(
|
|
Metrics::kMetricWifiUserInitiatedConnectionResult,
|
|
Metrics::kUserInitiatedConnectionResultSuccess));
|
|
EXPECT_CALL(*metrics(), NotifyUserInitiatedConnectionFailureReason(_, _))
|
|
.Times(0);
|
|
service_->SetState(Service::kStateConnected);
|
|
Mock::VerifyAndClearExpectations(metrics());
|
|
|
|
// User-initiated connection attempt failed.
|
|
service_->SetState(Service::kStateIdle);
|
|
service_->UserInitiatedConnect(&error);
|
|
EXPECT_CALL(*metrics(), NotifyUserInitiatedConnectionResult(
|
|
Metrics::kMetricWifiUserInitiatedConnectionResult,
|
|
Metrics::kUserInitiatedConnectionResultFailure));
|
|
EXPECT_CALL(*metrics(), NotifyUserInitiatedConnectionFailureReason(
|
|
Metrics::kMetricWifiUserInitiatedConnectionFailureReason,
|
|
Service::kFailureDHCP));
|
|
service_->SetFailure(Service::kFailureDHCP);
|
|
Mock::VerifyAndClearExpectations(metrics());
|
|
|
|
// User-initiated connection attempt aborted.
|
|
service_->SetState(Service::kStateIdle);
|
|
service_->UserInitiatedConnect(&error);
|
|
service_->SetState(Service::kStateAssociating);
|
|
EXPECT_CALL(*metrics(), NotifyUserInitiatedConnectionResult(
|
|
Metrics::kMetricWifiUserInitiatedConnectionResult,
|
|
Metrics::kUserInitiatedConnectionResultAborted));
|
|
EXPECT_CALL(*metrics(), NotifyUserInitiatedConnectionFailureReason(_, _))
|
|
.Times(0);
|
|
service_->SetState(Service::kStateIdle);
|
|
Mock::VerifyAndClearExpectations(metrics());
|
|
|
|
// No metric reporting for other state transition.
|
|
service_->SetState(Service::kStateIdle);
|
|
service_->UserInitiatedConnect(&error);
|
|
EXPECT_CALL(*metrics(), NotifyUserInitiatedConnectionResult(_, _)).Times(0);
|
|
EXPECT_CALL(*metrics(), NotifyUserInitiatedConnectionFailureReason(_, _))
|
|
.Times(0);
|
|
service_->SetState(Service::kStateAssociating);
|
|
service_->SetState(Service::kStateConfiguring);
|
|
Mock::VerifyAndClearExpectations(metrics());
|
|
|
|
// No metric reporting for non-user-initiated connection.
|
|
service_->SetState(Service::kStateIdle);
|
|
service_->Connect(&error, "in test");
|
|
EXPECT_CALL(*metrics(), NotifyUserInitiatedConnectionResult(_, _)).Times(0);
|
|
EXPECT_CALL(*metrics(), NotifyUserInitiatedConnectionFailureReason(_, _))
|
|
.Times(0);
|
|
service_->SetState(Service::kStateConnected);
|
|
Mock::VerifyAndClearExpectations(metrics());
|
|
|
|
// No metric reporting for other technology.
|
|
service_->technology_ = Technology::kCellular;
|
|
service_->SetState(Service::kStateIdle);
|
|
service_->UserInitiatedConnect(&error);
|
|
EXPECT_CALL(*metrics(), NotifyUserInitiatedConnectionResult(_, _)).Times(0);
|
|
EXPECT_CALL(*metrics(), NotifyUserInitiatedConnectionFailureReason(_, _))
|
|
.Times(0);
|
|
service_->SetFailure(Service::kFailureDHCP);
|
|
Mock::VerifyAndClearExpectations(metrics());
|
|
}
|
|
|
|
TEST_F(ServiceTest, ActivateCellularModem) {
|
|
ResultCallback callback =
|
|
Bind(&ServiceTest::TestCallback, Unretained(this));
|
|
EXPECT_CALL(*this, TestCallback(_)).Times(0);
|
|
Error error;
|
|
service_->ActivateCellularModem("Carrier", &error, callback);
|
|
EXPECT_TRUE(error.IsFailure());
|
|
}
|
|
|
|
TEST_F(ServiceTest, CompleteCellularActivation) {
|
|
Error error;
|
|
service_->CompleteCellularActivation(&error);
|
|
EXPECT_EQ(Error::kNotSupported, error.type());
|
|
}
|
|
|
|
TEST_F(ServiceTest, EnableAndRetainAutoConnect) {
|
|
EXPECT_FALSE(service_->retain_auto_connect());
|
|
EXPECT_FALSE(service_->auto_connect());
|
|
|
|
service_->EnableAndRetainAutoConnect();
|
|
EXPECT_TRUE(service_->retain_auto_connect());
|
|
EXPECT_TRUE(service_->auto_connect());
|
|
}
|
|
|
|
TEST_F(ServiceTest, ReRetainAutoConnect) {
|
|
service_->EnableAndRetainAutoConnect();
|
|
EXPECT_TRUE(service_->retain_auto_connect());
|
|
EXPECT_TRUE(service_->auto_connect());
|
|
|
|
service_->SetAutoConnect(false);
|
|
service_->EnableAndRetainAutoConnect();
|
|
EXPECT_TRUE(service_->retain_auto_connect());
|
|
EXPECT_FALSE(service_->auto_connect());
|
|
}
|
|
|
|
TEST_F(ServiceTest, IsAutoConnectable) {
|
|
const char* reason = nullptr;
|
|
service_->SetConnectable(true);
|
|
|
|
// Services with non-primary connectivity technologies should not auto-connect
|
|
// when the system is offline.
|
|
EXPECT_EQ(Technology::kUnknown, service_->technology());
|
|
EXPECT_CALL(mock_manager_, IsConnected()).WillOnce(Return(false));
|
|
EXPECT_FALSE(service_->IsAutoConnectable(&reason));
|
|
EXPECT_STREQ(Service::kAutoConnOffline, reason);
|
|
|
|
service_->technology_ = Technology::kEthernet;
|
|
EXPECT_TRUE(service_->IsAutoConnectable(&reason));
|
|
|
|
// We should not auto-connect to a Service that a user has
|
|
// deliberately disconnected.
|
|
Error error;
|
|
service_->UserInitiatedDisconnect(&error);
|
|
EXPECT_FALSE(service_->IsAutoConnectable(&reason));
|
|
EXPECT_STREQ(Service::kAutoConnExplicitDisconnect, reason);
|
|
|
|
// But if the Service is reloaded, it is eligible for auto-connect
|
|
// again.
|
|
NiceMock<MockStore> storage;
|
|
EXPECT_CALL(storage, ContainsGroup(storage_id_)).WillOnce(Return(true));
|
|
#if !defined(DISABLE_WIFI) || !defined(DISABLE_WIRED_8021X)
|
|
EXPECT_CALL(*eap_, Load(&storage, storage_id_));
|
|
#endif // DISABLE_WIFI || DISABLE_WIRED_8021X
|
|
EXPECT_TRUE(service_->Load(&storage));
|
|
EXPECT_TRUE(service_->IsAutoConnectable(&reason));
|
|
|
|
// A deliberate Connect should also re-enable auto-connect.
|
|
service_->UserInitiatedDisconnect(&error);
|
|
EXPECT_FALSE(service_->IsAutoConnectable(&reason));
|
|
service_->Connect(&error, "in test");
|
|
EXPECT_TRUE(service_->IsAutoConnectable(&reason));
|
|
|
|
// A non-user initiated Disconnect doesn't change anything.
|
|
service_->Disconnect(&error, "in test");
|
|
EXPECT_TRUE(service_->IsAutoConnectable(&reason));
|
|
|
|
// A resume also re-enables auto-connect.
|
|
service_->UserInitiatedDisconnect(&error);
|
|
EXPECT_FALSE(service_->IsAutoConnectable(&reason));
|
|
service_->OnAfterResume();
|
|
EXPECT_TRUE(service_->IsAutoConnectable(&reason));
|
|
|
|
service_->SetState(Service::kStateConnected);
|
|
EXPECT_FALSE(service_->IsAutoConnectable(&reason));
|
|
EXPECT_STREQ(Service::kAutoConnConnected, reason);
|
|
|
|
service_->SetState(Service::kStateAssociating);
|
|
EXPECT_FALSE(service_->IsAutoConnectable(&reason));
|
|
EXPECT_STREQ(Service::kAutoConnConnecting, reason);
|
|
|
|
service_->SetState(Service::kStateIdle);
|
|
EXPECT_CALL(mock_manager_, IsTechnologyAutoConnectDisabled(
|
|
service_->technology_)).WillOnce(Return(true));
|
|
EXPECT_FALSE(service_->IsAutoConnectable(&reason));
|
|
EXPECT_STREQ(Service::kAutoConnTechnologyNotConnectable, reason);
|
|
}
|
|
|
|
TEST_F(ServiceTest, AutoConnectLogging) {
|
|
ScopedMockLog log;
|
|
EXPECT_CALL(log, Log(_, _, _));
|
|
service_->SetConnectable(true);
|
|
|
|
ScopeLogger::GetInstance()->EnableScopesByName("+service");
|
|
ScopeLogger::GetInstance()->set_verbose_level(1);
|
|
service_->SetState(Service::kStateConnected);
|
|
EXPECT_CALL(log, Log(-1, _, HasSubstr(Service::kAutoConnConnected)));
|
|
service_->AutoConnect();
|
|
|
|
ScopeLogger::GetInstance()->EnableScopesByName("-service");
|
|
ScopeLogger::GetInstance()->set_verbose_level(0);
|
|
EXPECT_CALL(log, Log(logging::LOG_INFO, _,
|
|
HasSubstr(Service::kAutoConnNotConnectable)));
|
|
service_->SetConnectable(false);
|
|
service_->AutoConnect();
|
|
}
|
|
|
|
|
|
TEST_F(AllMockServiceTest, AutoConnectWithFailures) {
|
|
const char* reason;
|
|
service_->SetConnectable(true);
|
|
service_->technology_ = Technology::kEthernet;
|
|
EXPECT_TRUE(service_->IsAutoConnectable(&reason));
|
|
|
|
// The very first AutoConnect() doesn't trigger any throttling.
|
|
EXPECT_CALL(dispatcher_, PostDelayedTask(_, _)).Times(0);
|
|
service_->AutoConnect();
|
|
Mock::VerifyAndClearExpectations(&dispatcher_);
|
|
EXPECT_TRUE(service_->IsAutoConnectable(&reason));
|
|
|
|
// The second call does trigger some throttling.
|
|
EXPECT_CALL(dispatcher_, PostDelayedTask(_,
|
|
Service::kMinAutoConnectCooldownTimeMilliseconds));
|
|
service_->AutoConnect();
|
|
Mock::VerifyAndClearExpectations(&dispatcher_);
|
|
EXPECT_FALSE(service_->IsAutoConnectable(&reason));
|
|
EXPECT_STREQ(Service::kAutoConnThrottled, reason);
|
|
|
|
// Calling AutoConnect() again before the cooldown terminates does not change
|
|
// the timeout.
|
|
EXPECT_CALL(dispatcher_, PostDelayedTask(_, _)).Times(0);
|
|
service_->AutoConnect();
|
|
Mock::VerifyAndClearExpectations(&dispatcher_);
|
|
EXPECT_FALSE(service_->IsAutoConnectable(&reason));
|
|
EXPECT_STREQ(Service::kAutoConnThrottled, reason);
|
|
|
|
// Once the timeout expires, we can AutoConnect() again.
|
|
service_->ReEnableAutoConnectTask();
|
|
EXPECT_TRUE(service_->IsAutoConnectable(&reason));
|
|
|
|
// Timeouts increase exponentially.
|
|
uint64_t next_cooldown_time = service_->auto_connect_cooldown_milliseconds_;
|
|
EXPECT_EQ(next_cooldown_time,
|
|
Service::kAutoConnectCooldownBackoffFactor *
|
|
Service::kMinAutoConnectCooldownTimeMilliseconds);
|
|
while (next_cooldown_time <=
|
|
Service::kMaxAutoConnectCooldownTimeMilliseconds) {
|
|
EXPECT_CALL(dispatcher_, PostDelayedTask(_, next_cooldown_time));
|
|
service_->AutoConnect();
|
|
Mock::VerifyAndClearExpectations(&dispatcher_);
|
|
EXPECT_FALSE(service_->IsAutoConnectable(&reason));
|
|
EXPECT_STREQ(Service::kAutoConnThrottled, reason);
|
|
service_->ReEnableAutoConnectTask();
|
|
next_cooldown_time *= Service::kAutoConnectCooldownBackoffFactor;
|
|
}
|
|
|
|
// Once we hit our cap, future timeouts are the same.
|
|
for (int32_t i = 0; i < 2; i++) {
|
|
EXPECT_CALL(dispatcher_, PostDelayedTask(_,
|
|
Service::kMaxAutoConnectCooldownTimeMilliseconds));
|
|
service_->AutoConnect();
|
|
Mock::VerifyAndClearExpectations(&dispatcher_);
|
|
EXPECT_FALSE(service_->IsAutoConnectable(&reason));
|
|
EXPECT_STREQ(Service::kAutoConnThrottled, reason);
|
|
service_->ReEnableAutoConnectTask();
|
|
}
|
|
|
|
// Connecting successfully resets our cooldown.
|
|
service_->SetState(Service::kStateConnected);
|
|
service_->SetState(Service::kStateIdle);
|
|
reason = "";
|
|
EXPECT_TRUE(service_->IsAutoConnectable(&reason));
|
|
EXPECT_STREQ("", reason);
|
|
EXPECT_EQ(service_->auto_connect_cooldown_milliseconds_, 0);
|
|
|
|
// But future AutoConnects behave as before
|
|
EXPECT_CALL(dispatcher_, PostDelayedTask(_,
|
|
Service::kMinAutoConnectCooldownTimeMilliseconds)).Times(1);
|
|
service_->AutoConnect();
|
|
service_->AutoConnect();
|
|
Mock::VerifyAndClearExpectations(&dispatcher_);
|
|
EXPECT_FALSE(service_->IsAutoConnectable(&reason));
|
|
EXPECT_STREQ(Service::kAutoConnThrottled, reason);
|
|
|
|
// Cooldowns are forgotten if we go through a suspend/resume cycle.
|
|
service_->OnAfterResume();
|
|
reason = "";
|
|
EXPECT_TRUE(service_->IsAutoConnectable(&reason));
|
|
EXPECT_STREQ("", reason);
|
|
}
|
|
|
|
TEST_F(ServiceTest, ConfigureBadProperty) {
|
|
KeyValueStore args;
|
|
args.SetString("XXXInvalid", "Value");
|
|
Error error;
|
|
service_->Configure(args, &error);
|
|
EXPECT_FALSE(error.IsSuccess());
|
|
}
|
|
|
|
TEST_F(ServiceTest, ConfigureBoolProperty) {
|
|
service_->EnableAndRetainAutoConnect();
|
|
service_->SetAutoConnect(false);
|
|
ASSERT_FALSE(service_->auto_connect());
|
|
KeyValueStore args;
|
|
args.SetBool(kAutoConnectProperty, true);
|
|
Error error;
|
|
service_->Configure(args, &error);
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
EXPECT_TRUE(service_->auto_connect());
|
|
}
|
|
|
|
TEST_F(ServiceTest, ConfigureStringProperty) {
|
|
const string kGuid0 = "guid_zero";
|
|
const string kGuid1 = "guid_one";
|
|
service_->SetGuid(kGuid0, nullptr);
|
|
ASSERT_EQ(kGuid0, service_->guid());
|
|
KeyValueStore args;
|
|
args.SetString(kGuidProperty, kGuid1);
|
|
Error error;
|
|
service_->Configure(args, &error);
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
EXPECT_EQ(kGuid1, service_->guid());
|
|
}
|
|
|
|
TEST_F(ServiceTest, ConfigureStringsProperty) {
|
|
const vector<string> kStrings0{ "string0", "string1" };
|
|
const vector<string> kStrings1{ "string2", "string3" };
|
|
service_->set_strings(kStrings0);
|
|
ASSERT_EQ(kStrings0, service_->strings());
|
|
KeyValueStore args;
|
|
args.SetStrings(ServiceUnderTest::kStringsProperty, kStrings1);
|
|
Error error;
|
|
service_->Configure(args, &error);
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
EXPECT_EQ(kStrings1, service_->strings());
|
|
}
|
|
|
|
#if !defined(DISABLE_WIFI) || !defined(DISABLE_WIRED_8021X)
|
|
TEST_F(ServiceTest, ConfigureEapStringProperty) {
|
|
MockEapCredentials* eap = new MockEapCredentials();
|
|
service2_->SetEapCredentials(eap); // Passes ownership.
|
|
|
|
const string kEAPManagement0 = "management_zero";
|
|
const string kEAPManagement1 = "management_one";
|
|
service2_->SetEAPKeyManagement(kEAPManagement0);
|
|
|
|
EXPECT_CALL(*eap, key_management())
|
|
.WillOnce(ReturnRef(kEAPManagement0));
|
|
ASSERT_EQ(kEAPManagement0, service2_->GetEAPKeyManagement());
|
|
KeyValueStore args;
|
|
EXPECT_CALL(*eap, SetKeyManagement(kEAPManagement1, _));
|
|
args.SetString(kEapKeyMgmtProperty, kEAPManagement1);
|
|
Error error;
|
|
service2_->Configure(args, &error);
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
}
|
|
#endif // DISABLE_WIFI || DISABLE_WIRED_8021X
|
|
|
|
TEST_F(ServiceTest, ConfigureIntProperty) {
|
|
const int kPriority0 = 100;
|
|
const int kPriority1 = 200;
|
|
service_->SetPriority(kPriority0, nullptr);
|
|
ASSERT_EQ(kPriority0, service_->priority());
|
|
KeyValueStore args;
|
|
args.SetInt(kPriorityProperty, kPriority1);
|
|
Error error;
|
|
service_->Configure(args, &error);
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
EXPECT_EQ(kPriority1, service_->priority());
|
|
}
|
|
|
|
TEST_F(ServiceTest, ConfigureIgnoredProperty) {
|
|
service_->EnableAndRetainAutoConnect();
|
|
service_->SetAutoConnect(false);
|
|
ASSERT_FALSE(service_->auto_connect());
|
|
KeyValueStore args;
|
|
args.SetBool(kAutoConnectProperty, true);
|
|
Error error;
|
|
service_->IgnoreParameterForConfigure(kAutoConnectProperty);
|
|
service_->Configure(args, &error);
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
EXPECT_FALSE(service_->auto_connect());
|
|
}
|
|
|
|
TEST_F(ServiceTest, ConfigureProfileProperty) {
|
|
// Ensure that the Profile property is always ignored.
|
|
KeyValueStore args;
|
|
args.SetString(kProfileProperty, "profile");
|
|
Error error;
|
|
EXPECT_CALL(mock_manager_, SetProfileForService(_, _, _)).Times(0);
|
|
service_->Configure(args, &error);
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
}
|
|
|
|
TEST_F(ServiceTest, ConfigureKeyValueStoreProperty) {
|
|
KeyValueStore key_value_store0;
|
|
key_value_store0.SetBool("key0", true);
|
|
KeyValueStore key_value_store1;
|
|
key_value_store1.SetInt("key1", 1);
|
|
service_->SetKeyValueStore(key_value_store0, NULL);
|
|
ASSERT_EQ(key_value_store0, service_->GetKeyValueStore(NULL));
|
|
KeyValueStore args;
|
|
args.SetKeyValueStore(
|
|
ServiceUnderTest::kKeyValueStoreProperty, key_value_store1);
|
|
Error error;
|
|
service_->Configure(args, &error);
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
EXPECT_EQ(key_value_store1, service_->GetKeyValueStore(NULL));
|
|
}
|
|
|
|
TEST_F(ServiceTest, DoPropertiesMatch) {
|
|
service_->SetAutoConnect(false);
|
|
const string kGUID0 = "guid_zero";
|
|
const string kGUID1 = "guid_one";
|
|
service_->SetGuid(kGUID0, nullptr);
|
|
const uint32_t kPriority0 = 100;
|
|
const uint32_t kPriority1 = 200;
|
|
service_->SetPriority(kPriority0, nullptr);
|
|
const vector<string> kStrings0{ "string0", "string1" };
|
|
const vector<string> kStrings1{ "string2", "string3" };
|
|
service_->set_strings(kStrings0);
|
|
KeyValueStore key_value_store0;
|
|
key_value_store0.SetBool("key0", true);
|
|
KeyValueStore key_value_store1;
|
|
key_value_store1.SetInt("key1", 1);
|
|
service_->SetKeyValueStore(key_value_store0, NULL);
|
|
|
|
{
|
|
KeyValueStore args;
|
|
args.SetString(kGuidProperty, kGUID0);
|
|
args.SetBool(kAutoConnectProperty, false);
|
|
args.SetInt(kPriorityProperty, kPriority0);
|
|
args.SetStrings(ServiceUnderTest::kStringsProperty, kStrings0);
|
|
args.SetKeyValueStore(ServiceUnderTest::kKeyValueStoreProperty,
|
|
key_value_store0);
|
|
EXPECT_TRUE(service_->DoPropertiesMatch(args));
|
|
}
|
|
{
|
|
KeyValueStore args;
|
|
args.SetString(kGuidProperty, kGUID1);
|
|
args.SetBool(kAutoConnectProperty, false);
|
|
args.SetInt(kPriorityProperty, kPriority0);
|
|
args.SetStrings(ServiceUnderTest::kStringsProperty, kStrings0);
|
|
args.SetKeyValueStore(ServiceUnderTest::kKeyValueStoreProperty,
|
|
key_value_store0);
|
|
EXPECT_FALSE(service_->DoPropertiesMatch(args));
|
|
}
|
|
{
|
|
KeyValueStore args;
|
|
args.SetString(kGuidProperty, kGUID0);
|
|
args.SetBool(kAutoConnectProperty, true);
|
|
args.SetInt(kPriorityProperty, kPriority0);
|
|
args.SetStrings(ServiceUnderTest::kStringsProperty, kStrings0);
|
|
args.SetKeyValueStore(ServiceUnderTest::kKeyValueStoreProperty,
|
|
key_value_store0);
|
|
EXPECT_FALSE(service_->DoPropertiesMatch(args));
|
|
}
|
|
{
|
|
KeyValueStore args;
|
|
args.SetString(kGuidProperty, kGUID0);
|
|
args.SetBool(kAutoConnectProperty, false);
|
|
args.SetInt(kPriorityProperty, kPriority1);
|
|
args.SetStrings(ServiceUnderTest::kStringsProperty, kStrings0);
|
|
args.SetKeyValueStore(ServiceUnderTest::kKeyValueStoreProperty,
|
|
key_value_store0);
|
|
EXPECT_FALSE(service_->DoPropertiesMatch(args));
|
|
}
|
|
{
|
|
KeyValueStore args;
|
|
args.SetString(kGuidProperty, kGUID0);
|
|
args.SetBool(kAutoConnectProperty, false);
|
|
args.SetInt(kPriorityProperty, kPriority0);
|
|
args.SetStrings(ServiceUnderTest::kStringsProperty, kStrings1);
|
|
args.SetKeyValueStore(ServiceUnderTest::kKeyValueStoreProperty,
|
|
key_value_store0);
|
|
EXPECT_FALSE(service_->DoPropertiesMatch(args));
|
|
}
|
|
{
|
|
KeyValueStore args;
|
|
args.SetString(kGuidProperty, kGUID0);
|
|
args.SetBool(kAutoConnectProperty, false);
|
|
args.SetInt(kPriorityProperty, kPriority0);
|
|
args.SetStrings(ServiceUnderTest::kStringsProperty, kStrings0);
|
|
args.SetKeyValueStore(ServiceUnderTest::kKeyValueStoreProperty,
|
|
key_value_store1);
|
|
EXPECT_FALSE(service_->DoPropertiesMatch(args));
|
|
}
|
|
}
|
|
|
|
TEST_F(ServiceTest, IsRemembered) {
|
|
service_->set_profile(nullptr);
|
|
EXPECT_CALL(mock_manager_, IsServiceEphemeral(_)).Times(0);
|
|
EXPECT_FALSE(service_->IsRemembered());
|
|
|
|
scoped_refptr<MockProfile> profile(
|
|
new StrictMock<MockProfile>(control_interface(), metrics(), manager()));
|
|
service_->set_profile(profile);
|
|
EXPECT_CALL(mock_manager_, IsServiceEphemeral(IsRefPtrTo(service_)))
|
|
.WillOnce(Return(true))
|
|
.WillOnce(Return(false));
|
|
EXPECT_FALSE(service_->IsRemembered());
|
|
EXPECT_TRUE(service_->IsRemembered());
|
|
}
|
|
|
|
TEST_F(ServiceTest, IsDependentOn) {
|
|
EXPECT_FALSE(service_->IsDependentOn(nullptr));
|
|
|
|
std::unique_ptr<MockDeviceInfo> mock_device_info(
|
|
new NiceMock<MockDeviceInfo>(control_interface(), dispatcher(), metrics(),
|
|
&mock_manager_));
|
|
scoped_refptr<MockConnection> mock_connection0(
|
|
new NiceMock<MockConnection>(mock_device_info.get()));
|
|
scoped_refptr<MockConnection> mock_connection1(
|
|
new NiceMock<MockConnection>(mock_device_info.get()));
|
|
|
|
service_->connection_ = mock_connection0;
|
|
EXPECT_CALL(*mock_connection0, GetLowerConnection())
|
|
.WillRepeatedly(Return(mock_connection1));
|
|
EXPECT_CALL(*mock_connection1, GetLowerConnection())
|
|
.WillRepeatedly(Return(ConnectionRefPtr()));
|
|
EXPECT_FALSE(service_->IsDependentOn(nullptr));
|
|
|
|
scoped_refptr<ServiceUnderTest> service1 =
|
|
new ServiceUnderTest(control_interface(),
|
|
dispatcher(),
|
|
metrics(),
|
|
&mock_manager_);
|
|
EXPECT_FALSE(service_->IsDependentOn(service1));
|
|
|
|
service1->connection_ = mock_connection0;
|
|
EXPECT_FALSE(service_->IsDependentOn(service1));
|
|
|
|
service1->connection_ = mock_connection1;
|
|
EXPECT_TRUE(service_->IsDependentOn(service1));
|
|
|
|
service_->connection_ = mock_connection1;
|
|
service1->connection_ = nullptr;
|
|
EXPECT_FALSE(service_->IsDependentOn(service1));
|
|
|
|
service_->connection_ = nullptr;
|
|
}
|
|
|
|
TEST_F(ServiceTest, OnPropertyChanged) {
|
|
scoped_refptr<MockProfile> profile(
|
|
new StrictMock<MockProfile>(control_interface(), metrics(), manager()));
|
|
service_->set_profile(nullptr);
|
|
// Expect no crash.
|
|
service_->OnPropertyChanged("");
|
|
|
|
// Expect no call to Update if the profile has no storage.
|
|
service_->set_profile(profile);
|
|
EXPECT_CALL(*profile, UpdateService(_)).Times(0);
|
|
EXPECT_CALL(*profile, GetConstStorage()).WillOnce(Return(nullptr));
|
|
service_->OnPropertyChanged("");
|
|
|
|
// Expect call to Update if the profile has storage.
|
|
EXPECT_CALL(*profile, UpdateService(_)).Times(1);
|
|
NiceMock<MockStore> storage;
|
|
EXPECT_CALL(*profile, GetConstStorage()).WillOnce(Return(&storage));
|
|
service_->OnPropertyChanged("");
|
|
}
|
|
|
|
|
|
TEST_F(ServiceTest, RecheckPortal) {
|
|
service_->state_ = Service::kStateIdle;
|
|
EXPECT_CALL(mock_manager_, RecheckPortalOnService(_)).Times(0);
|
|
service_->OnPropertyChanged(kCheckPortalProperty);
|
|
|
|
service_->state_ = Service::kStatePortal;
|
|
EXPECT_CALL(mock_manager_, RecheckPortalOnService(IsRefPtrTo(service_)))
|
|
.Times(1);
|
|
service_->OnPropertyChanged(kCheckPortalProperty);
|
|
|
|
service_->state_ = Service::kStateConnected;
|
|
EXPECT_CALL(mock_manager_, RecheckPortalOnService(IsRefPtrTo(service_)))
|
|
.Times(1);
|
|
service_->OnPropertyChanged(kProxyConfigProperty);
|
|
|
|
service_->state_ = Service::kStateOnline;
|
|
EXPECT_CALL(mock_manager_, RecheckPortalOnService(IsRefPtrTo(service_)))
|
|
.Times(1);
|
|
service_->OnPropertyChanged(kCheckPortalProperty);
|
|
|
|
service_->state_ = Service::kStatePortal;
|
|
EXPECT_CALL(mock_manager_, RecheckPortalOnService(_)).Times(0);
|
|
service_->OnPropertyChanged(kEapKeyIdProperty);
|
|
}
|
|
|
|
TEST_F(ServiceTest, SetCheckPortal) {
|
|
{
|
|
Error error;
|
|
service_->SetCheckPortal("false", &error);
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
EXPECT_EQ(Service::kCheckPortalFalse, service_->check_portal_);
|
|
}
|
|
{
|
|
Error error;
|
|
service_->SetCheckPortal("true", &error);
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
EXPECT_EQ(Service::kCheckPortalTrue, service_->check_portal_);
|
|
}
|
|
{
|
|
Error error;
|
|
service_->SetCheckPortal("auto", &error);
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
EXPECT_EQ(Service::kCheckPortalAuto, service_->check_portal_);
|
|
}
|
|
{
|
|
Error error;
|
|
service_->SetCheckPortal("xxx", &error);
|
|
EXPECT_FALSE(error.IsSuccess());
|
|
EXPECT_EQ(Error::kInvalidArguments, error.type());
|
|
EXPECT_EQ(Service::kCheckPortalAuto, service_->check_portal_);
|
|
}
|
|
}
|
|
|
|
TEST_F(ServiceTest, SetFriendlyName) {
|
|
EXPECT_EQ(service_->unique_name_, service_->friendly_name_);
|
|
ServiceMockAdaptor* adaptor = GetAdaptor();
|
|
|
|
EXPECT_CALL(*adaptor, EmitStringChanged(_, _)).Times(0);
|
|
service_->SetFriendlyName(service_->unique_name_);
|
|
EXPECT_EQ(service_->unique_name_, service_->friendly_name_);
|
|
|
|
EXPECT_CALL(*adaptor, EmitStringChanged(kNameProperty,
|
|
"Test Name 1"));
|
|
service_->SetFriendlyName("Test Name 1");
|
|
EXPECT_EQ("Test Name 1", service_->friendly_name_);
|
|
|
|
EXPECT_CALL(*adaptor, EmitStringChanged(_, _)).Times(0);
|
|
service_->SetFriendlyName("Test Name 1");
|
|
EXPECT_EQ("Test Name 1", service_->friendly_name_);
|
|
|
|
EXPECT_CALL(*adaptor, EmitStringChanged(kNameProperty,
|
|
"Test Name 2"));
|
|
service_->SetFriendlyName("Test Name 2");
|
|
EXPECT_EQ("Test Name 2", service_->friendly_name_);
|
|
}
|
|
|
|
TEST_F(ServiceTest, SetConnectableFull) {
|
|
EXPECT_FALSE(service_->connectable());
|
|
|
|
ServiceMockAdaptor* adaptor = GetAdaptor();
|
|
|
|
EXPECT_CALL(*adaptor, EmitBoolChanged(_, _)).Times(0);
|
|
EXPECT_CALL(mock_manager_, HasService(_)).Times(0);
|
|
service_->SetConnectableFull(false);
|
|
EXPECT_FALSE(service_->connectable());
|
|
|
|
EXPECT_CALL(*adaptor, EmitBoolChanged(kConnectableProperty, true));
|
|
EXPECT_CALL(mock_manager_, HasService(_)).WillOnce(Return(false));
|
|
EXPECT_CALL(mock_manager_, UpdateService(_)).Times(0);
|
|
service_->SetConnectableFull(true);
|
|
EXPECT_TRUE(service_->connectable());
|
|
|
|
EXPECT_CALL(*adaptor, EmitBoolChanged(kConnectableProperty, false));
|
|
EXPECT_CALL(mock_manager_, HasService(_)).WillOnce(Return(true));
|
|
EXPECT_CALL(mock_manager_, UpdateService(_));
|
|
service_->SetConnectableFull(false);
|
|
EXPECT_FALSE(service_->connectable());
|
|
|
|
EXPECT_CALL(*adaptor, EmitBoolChanged(kConnectableProperty, true));
|
|
EXPECT_CALL(mock_manager_, HasService(_)).WillOnce(Return(true));
|
|
EXPECT_CALL(mock_manager_, UpdateService(_));
|
|
service_->SetConnectableFull(true);
|
|
EXPECT_TRUE(service_->connectable());
|
|
}
|
|
|
|
#if !defined(DISABLE_WIFI) || !defined(DISABLE_WIRED_8021X)
|
|
class WriteOnlyServicePropertyTest : public ServiceTest {};
|
|
TEST_P(WriteOnlyServicePropertyTest, PropertyWriteOnly) {
|
|
// Use a real EapCredentials instance since the base Service class
|
|
// contains no write-only properties.
|
|
EapCredentials eap;
|
|
eap.InitPropertyStore(service_->mutable_store());
|
|
|
|
string property(GetParam().Get<string>());
|
|
Error error;
|
|
EXPECT_FALSE(service_->store().GetStringProperty(property, nullptr, &error));
|
|
EXPECT_EQ(Error::kPermissionDenied, error.type());
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(
|
|
WriteOnlyServicePropertyTestInstance,
|
|
WriteOnlyServicePropertyTest,
|
|
Values(
|
|
brillo::Any(string(kEapPrivateKeyPasswordProperty)),
|
|
brillo::Any(string(kEapPasswordProperty))));
|
|
#endif // DISABLE_WIFI || DISABLE_WIRED_8021X
|
|
|
|
TEST_F(ServiceTest, GetIPConfigRpcIdentifier) {
|
|
{
|
|
Error error;
|
|
EXPECT_EQ(control_interface()->NullRPCIdentifier(),
|
|
service_->GetIPConfigRpcIdentifier(&error));
|
|
EXPECT_EQ(Error::kNotFound, error.type());
|
|
}
|
|
|
|
std::unique_ptr<MockDeviceInfo> mock_device_info(
|
|
new NiceMock<MockDeviceInfo>(control_interface(), dispatcher(), metrics(),
|
|
&mock_manager_));
|
|
scoped_refptr<MockConnection> mock_connection(
|
|
new NiceMock<MockConnection>(mock_device_info.get()));
|
|
|
|
service_->connection_ = mock_connection;
|
|
|
|
{
|
|
Error error;
|
|
const string empty_string;
|
|
EXPECT_CALL(*mock_connection, ipconfig_rpc_identifier())
|
|
.WillOnce(ReturnRef(empty_string));
|
|
EXPECT_EQ(control_interface()->NullRPCIdentifier(),
|
|
service_->GetIPConfigRpcIdentifier(&error));
|
|
EXPECT_EQ(Error::kNotFound, error.type());
|
|
}
|
|
|
|
{
|
|
Error error;
|
|
const string nonempty_string("/ipconfig/path");
|
|
EXPECT_CALL(*mock_connection, ipconfig_rpc_identifier())
|
|
.WillOnce(ReturnRef(nonempty_string));
|
|
EXPECT_EQ(nonempty_string, service_->GetIPConfigRpcIdentifier(&error));
|
|
EXPECT_EQ(Error::kSuccess, error.type());
|
|
}
|
|
|
|
// Assure orderly destruction of the Connection before DeviceInfo.
|
|
service_->connection_ = nullptr;
|
|
mock_connection = nullptr;
|
|
mock_device_info.reset();
|
|
}
|
|
|
|
#if !defined(DISABLE_WIFI) || !defined(DISABLE_WIRED_8021X)
|
|
class ServiceWithMockOnEapCredentialsChanged : public ServiceUnderTest {
|
|
public:
|
|
ServiceWithMockOnEapCredentialsChanged(ControlInterface* control_interface,
|
|
EventDispatcher* dispatcher,
|
|
Metrics* metrics,
|
|
Manager* manager)
|
|
: ServiceUnderTest(control_interface, dispatcher, metrics, manager),
|
|
is_8021x_(false) {}
|
|
MOCK_METHOD1(OnEapCredentialsChanged, void(Service::UpdateCredentialsReason));
|
|
virtual bool Is8021x() const { return is_8021x_; }
|
|
void set_is_8021x(bool is_8021x) { is_8021x_ = is_8021x; }
|
|
|
|
private:
|
|
bool is_8021x_;
|
|
};
|
|
|
|
TEST_F(ServiceTest, SetEAPCredentialsOverRPC) {
|
|
scoped_refptr<ServiceWithMockOnEapCredentialsChanged> service(
|
|
new ServiceWithMockOnEapCredentialsChanged(control_interface(),
|
|
dispatcher(),
|
|
metrics(),
|
|
&mock_manager_));
|
|
string eap_credential_properties[] = {
|
|
kEapAnonymousIdentityProperty,
|
|
kEapCertIdProperty,
|
|
kEapClientCertProperty,
|
|
kEapIdentityProperty,
|
|
kEapKeyIdProperty,
|
|
kEapPasswordProperty,
|
|
kEapPinProperty,
|
|
kEapPrivateKeyProperty,
|
|
kEapPrivateKeyPasswordProperty
|
|
};
|
|
string eap_non_credential_properties[] = {
|
|
kEapCaCertIdProperty,
|
|
kEapCaCertNssProperty,
|
|
kEapMethodProperty,
|
|
kEapPhase2AuthProperty,
|
|
kEapUseSystemCasProperty
|
|
};
|
|
// While this is not an 802.1x-based service, none of these property
|
|
// changes should cause a call to set_eap().
|
|
EXPECT_CALL(*service, OnEapCredentialsChanged(_)).Times(0);
|
|
for (size_t i = 0; i < arraysize(eap_credential_properties); ++i)
|
|
service->OnPropertyChanged(eap_credential_properties[i]);
|
|
for (size_t i = 0; i < arraysize(eap_non_credential_properties); ++i)
|
|
service->OnPropertyChanged(eap_non_credential_properties[i]);
|
|
service->OnPropertyChanged(kEapKeyMgmtProperty);
|
|
|
|
service->set_is_8021x(true);
|
|
|
|
// When this is an 802.1x-based service, set_eap should be called for
|
|
// all credential-carrying properties.
|
|
for (size_t i = 0; i < arraysize(eap_credential_properties); ++i) {
|
|
EXPECT_CALL(*service,
|
|
OnEapCredentialsChanged(
|
|
Service::kReasonPropertyUpdate)).Times(1);
|
|
service->OnPropertyChanged(eap_credential_properties[i]);
|
|
Mock::VerifyAndClearExpectations(service.get());
|
|
}
|
|
|
|
// The key management property is a special case. While not strictly
|
|
// a credential, it can change which credentials are used. Therefore it
|
|
// should also trigger a call to set_eap();
|
|
EXPECT_CALL(*service,
|
|
OnEapCredentialsChanged(Service::kReasonPropertyUpdate)).Times(1);
|
|
service->OnPropertyChanged(kEapKeyMgmtProperty);
|
|
Mock::VerifyAndClearExpectations(service.get());
|
|
|
|
EXPECT_CALL(*service, OnEapCredentialsChanged(_)).Times(0);
|
|
for (size_t i = 0; i < arraysize(eap_non_credential_properties); ++i)
|
|
service->OnPropertyChanged(eap_non_credential_properties[i]);
|
|
}
|
|
|
|
TEST_F(ServiceTest, Certification) {
|
|
EXPECT_FALSE(service_->remote_certification_.size());
|
|
|
|
ScopedMockLog log;
|
|
EXPECT_CALL(log, Log(logging::LOG_WARNING, _,
|
|
HasSubstr("exceeds our maximum"))).Times(2);
|
|
string kSubject("foo");
|
|
EXPECT_FALSE(service_->AddEAPCertification(
|
|
kSubject, Service::kEAPMaxCertificationElements));
|
|
EXPECT_FALSE(service_->AddEAPCertification(
|
|
kSubject, Service::kEAPMaxCertificationElements + 1));
|
|
EXPECT_FALSE(service_->remote_certification_.size());
|
|
Mock::VerifyAndClearExpectations(&log);
|
|
|
|
EXPECT_CALL(log,
|
|
Log(logging::LOG_INFO, _, HasSubstr("Received certification")))
|
|
.Times(1);
|
|
EXPECT_TRUE(service_->AddEAPCertification(
|
|
kSubject, Service::kEAPMaxCertificationElements - 1));
|
|
Mock::VerifyAndClearExpectations(&log);
|
|
EXPECT_EQ(Service::kEAPMaxCertificationElements,
|
|
service_->remote_certification_.size());
|
|
for (size_t i = 0; i < Service::kEAPMaxCertificationElements - 1; ++i) {
|
|
EXPECT_TRUE(service_->remote_certification_[i].empty());
|
|
}
|
|
EXPECT_EQ(kSubject, service_->remote_certification_[
|
|
Service::kEAPMaxCertificationElements - 1]);
|
|
|
|
// Re-adding the same name in the same position should not generate a log.
|
|
EXPECT_CALL(log, Log(_, _, _)).Times(0);
|
|
EXPECT_TRUE(service_->AddEAPCertification(
|
|
kSubject, Service::kEAPMaxCertificationElements - 1));
|
|
|
|
// Replacing the item should generate a log message.
|
|
EXPECT_CALL(log,
|
|
Log(logging::LOG_INFO, _, HasSubstr("Received certification")))
|
|
.Times(1);
|
|
EXPECT_TRUE(service_->AddEAPCertification(
|
|
kSubject + "x", Service::kEAPMaxCertificationElements - 1));
|
|
|
|
service_->ClearEAPCertification();
|
|
EXPECT_TRUE(service_->remote_certification_.empty());
|
|
}
|
|
#endif // DISABLE_WIFI || DISABLE_WIRED_8021X
|
|
|
|
TEST_F(ServiceTest, NoteDisconnectEventIdle) {
|
|
Timestamp timestamp;
|
|
EXPECT_CALL(time_, GetNow()).Times(7).WillRepeatedly((Return(timestamp)));
|
|
SetStateField(Service::kStateOnline);
|
|
EXPECT_FALSE(service_->HasRecentConnectionIssues());
|
|
service_->SetState(Service::kStateIdle);
|
|
// The transition Online->Idle is not an event.
|
|
EXPECT_FALSE(service_->HasRecentConnectionIssues());
|
|
service_->SetState(Service::kStateFailure);
|
|
// The transition Online->Idle->Failure is a connection drop.
|
|
EXPECT_TRUE(service_->HasRecentConnectionIssues());
|
|
}
|
|
|
|
TEST_F(ServiceTest, NoteDisconnectEventOnSetStateFailure) {
|
|
Timestamp timestamp;
|
|
EXPECT_CALL(time_, GetNow()).Times(5).WillRepeatedly((Return(timestamp)));
|
|
SetStateField(Service::kStateOnline);
|
|
EXPECT_FALSE(service_->HasRecentConnectionIssues());
|
|
service_->SetState(Service::kStateFailure);
|
|
EXPECT_TRUE(service_->HasRecentConnectionIssues());
|
|
}
|
|
|
|
TEST_F(ServiceTest, NoteDisconnectEventOnSetFailureSilent) {
|
|
Timestamp timestamp;
|
|
EXPECT_CALL(time_, GetNow()).Times(5).WillRepeatedly((Return(timestamp)));
|
|
SetStateField(Service::kStateConfiguring);
|
|
EXPECT_FALSE(service_->HasRecentConnectionIssues());
|
|
service_->SetFailureSilent(Service::kFailureEAPAuthentication);
|
|
EXPECT_TRUE(service_->HasRecentConnectionIssues());
|
|
}
|
|
|
|
TEST_F(ServiceTest, NoteDisconnectEventNonEvent) {
|
|
EXPECT_CALL(time_, GetNow()).Times(0);
|
|
|
|
// Explicit disconnect is a non-event.
|
|
SetStateField(Service::kStateOnline);
|
|
SetExplicitlyDisconnected(true);
|
|
NoteDisconnectEvent();
|
|
EXPECT_TRUE(GetDisconnects()->Empty());
|
|
EXPECT_TRUE(GetMisconnects()->Empty());
|
|
|
|
// Failure to idle transition is a non-event.
|
|
SetStateField(Service::kStateFailure);
|
|
SetExplicitlyDisconnected(false);
|
|
NoteDisconnectEvent();
|
|
EXPECT_TRUE(GetDisconnects()->Empty());
|
|
EXPECT_TRUE(GetMisconnects()->Empty());
|
|
|
|
// Disconnect while manager is stopped is a non-event.
|
|
SetStateField(Service::kStateOnline);
|
|
SetManagerRunning(false);
|
|
NoteDisconnectEvent();
|
|
EXPECT_TRUE(GetDisconnects()->Empty());
|
|
EXPECT_TRUE(GetMisconnects()->Empty());
|
|
|
|
// Disconnect while suspending is a non-event.
|
|
SetManagerRunning(true);
|
|
SetSuspending(true);
|
|
NoteDisconnectEvent();
|
|
EXPECT_TRUE(GetDisconnects()->Empty());
|
|
EXPECT_TRUE(GetMisconnects()->Empty());
|
|
}
|
|
|
|
TEST_F(ServiceTest, NoteDisconnectEventDisconnectOnce) {
|
|
const int kNow = 5;
|
|
EXPECT_FALSE(service_->explicitly_disconnected());
|
|
SetStateField(Service::kStateOnline);
|
|
EXPECT_CALL(time_, GetNow()).WillOnce(Return(GetTimestamp(kNow, kNow, "")));
|
|
NoteDisconnectEvent();
|
|
ASSERT_EQ(1, GetDisconnects()->Size());
|
|
EXPECT_EQ(kNow, GetDisconnects()->Front().monotonic.tv_sec);
|
|
EXPECT_TRUE(GetMisconnects()->Empty());
|
|
|
|
Mock::VerifyAndClearExpectations(&time_);
|
|
EXPECT_CALL(time_, GetNow()).Times(2).WillRepeatedly(Return(GetTimestamp(
|
|
kNow + GetDisconnectsMonitorSeconds() - 1,
|
|
kNow + GetDisconnectsMonitorSeconds() - 1,
|
|
"")));
|
|
EXPECT_TRUE(service_->HasRecentConnectionIssues());
|
|
ASSERT_EQ(1, GetDisconnects()->Size());
|
|
|
|
Mock::VerifyAndClearExpectations(&time_);
|
|
EXPECT_CALL(time_, GetNow()).Times(2).WillRepeatedly(Return(GetTimestamp(
|
|
kNow + GetDisconnectsMonitorSeconds(),
|
|
kNow + GetDisconnectsMonitorSeconds(),
|
|
"")));
|
|
EXPECT_FALSE(service_->HasRecentConnectionIssues());
|
|
ASSERT_TRUE(GetDisconnects()->Empty());
|
|
}
|
|
|
|
TEST_F(ServiceTest, NoteDisconnectEventMisconnectOnce) {
|
|
const int kNow = 7;
|
|
EXPECT_FALSE(service_->explicitly_disconnected());
|
|
SetStateField(Service::kStateConfiguring);
|
|
EXPECT_CALL(time_, GetNow()).WillOnce(Return(GetTimestamp(kNow, kNow, "")));
|
|
NoteDisconnectEvent();
|
|
EXPECT_TRUE(GetDisconnects()->Empty());
|
|
ASSERT_EQ(1, GetMisconnects()->Size());
|
|
EXPECT_EQ(kNow, GetMisconnects()->Front().monotonic.tv_sec);
|
|
|
|
Mock::VerifyAndClearExpectations(&time_);
|
|
EXPECT_CALL(time_, GetNow()).Times(2).WillRepeatedly(Return(GetTimestamp(
|
|
kNow + GetMisconnectsMonitorSeconds() - 1,
|
|
kNow + GetMisconnectsMonitorSeconds() - 1,
|
|
"")));
|
|
EXPECT_TRUE(service_->HasRecentConnectionIssues());
|
|
ASSERT_EQ(1, GetMisconnects()->Size());
|
|
|
|
Mock::VerifyAndClearExpectations(&time_);
|
|
EXPECT_CALL(time_, GetNow()).Times(2).WillRepeatedly(Return(GetTimestamp(
|
|
kNow + GetMisconnectsMonitorSeconds(),
|
|
kNow + GetMisconnectsMonitorSeconds(),
|
|
"")));
|
|
EXPECT_FALSE(service_->HasRecentConnectionIssues());
|
|
ASSERT_TRUE(GetMisconnects()->Empty());
|
|
}
|
|
|
|
TEST_F(ServiceTest, NoteDisconnectEventDiscardOld) {
|
|
EXPECT_FALSE(service_->explicitly_disconnected());
|
|
for (int i = 0; i < 2; i++) {
|
|
int now = 0;
|
|
EventHistory* events = nullptr;
|
|
if (i == 0) {
|
|
SetStateField(Service::kStateConnected);
|
|
now = GetDisconnectsMonitorSeconds() + 1;
|
|
events = GetDisconnects();
|
|
} else {
|
|
SetStateField(Service::kStateAssociating);
|
|
now = GetMisconnectsMonitorSeconds() + 1;
|
|
events = GetMisconnects();
|
|
}
|
|
PushTimestamp(events, 0, 0, "");
|
|
PushTimestamp(events, 0, 0, "");
|
|
EXPECT_CALL(time_, GetNow()).WillOnce(Return(GetTimestamp(now, now, "")));
|
|
NoteDisconnectEvent();
|
|
ASSERT_EQ(1, events->Size());
|
|
EXPECT_EQ(now, events->Front().monotonic.tv_sec);
|
|
}
|
|
}
|
|
|
|
TEST_F(ServiceTest, NoteDisconnectEventDiscardExcessive) {
|
|
EXPECT_FALSE(service_->explicitly_disconnected());
|
|
SetStateField(Service::kStateOnline);
|
|
for (int i = 0; i < 2 * GetMaxDisconnectEventHistory(); i++) {
|
|
PushTimestamp(GetDisconnects(), 0, 0, "");
|
|
}
|
|
EXPECT_CALL(time_, GetNow()).WillOnce(Return(Timestamp()));
|
|
NoteDisconnectEvent();
|
|
EXPECT_EQ(GetMaxDisconnectEventHistory(), GetDisconnects()->Size());
|
|
}
|
|
|
|
TEST_F(ServiceTest, NoteMisconnectEventDiscardExcessive) {
|
|
EXPECT_FALSE(service_->explicitly_disconnected());
|
|
SetStateField(Service::kStateAssociating);
|
|
for (int i = 0; i < 2 * GetMaxMisconnectEventHistory(); i++) {
|
|
PushTimestamp(GetMisconnects(), 0, 0, "");
|
|
}
|
|
EXPECT_CALL(time_, GetNow()).WillOnce(Return(Timestamp()));
|
|
NoteDisconnectEvent();
|
|
EXPECT_EQ(GetMaxMisconnectEventHistory(), GetMisconnects()->Size());
|
|
}
|
|
|
|
TEST_F(ServiceTest, DiagnosticsProperties) {
|
|
const string kWallClock0 = "2012-12-09T12:41:22.234567-0800";
|
|
const string kWallClock1 = "2012-12-31T23:59:59.345678-0800";
|
|
Strings values;
|
|
|
|
PushTimestamp(GetDisconnects(), 0, 0, kWallClock0);
|
|
Error unused_error;
|
|
ASSERT_TRUE(service_->store().GetStringsProperty(
|
|
kDiagnosticsDisconnectsProperty, &values, &unused_error));
|
|
ASSERT_EQ(1, values.size());
|
|
EXPECT_EQ(kWallClock0, values[0]);
|
|
|
|
PushTimestamp(GetMisconnects(), 0, 0, kWallClock1);
|
|
ASSERT_TRUE(service_->store().GetStringsProperty(
|
|
kDiagnosticsMisconnectsProperty, &values, &unused_error));
|
|
ASSERT_EQ(1, values.size());
|
|
EXPECT_EQ(kWallClock1, values[0]);
|
|
}
|
|
|
|
TEST_F(ServiceTest, SecurityLevel) {
|
|
// Encrypted is better than not.
|
|
service_->SetSecurity(Service::kCryptoNone, false, false);
|
|
service2_->SetSecurity(Service::kCryptoRc4, false, false);
|
|
EXPECT_GT(service2_->SecurityLevel(), service_->SecurityLevel());
|
|
|
|
// AES encryption is better than RC4 encryption.
|
|
service_->SetSecurity(Service::kCryptoRc4, false, false);
|
|
service2_->SetSecurity(Service::kCryptoAes, false, false);
|
|
EXPECT_GT(service2_->SecurityLevel(), service_->SecurityLevel());
|
|
|
|
// Crypto algorithm is more important than key rotation.
|
|
service_->SetSecurity(Service::kCryptoNone, true, false);
|
|
service2_->SetSecurity(Service::kCryptoAes, false, false);
|
|
EXPECT_GT(service2_->SecurityLevel(), service_->SecurityLevel());
|
|
|
|
// Encrypted-but-unauthenticated is better than clear-but-authenticated.
|
|
service_->SetSecurity(Service::kCryptoNone, false, true);
|
|
service2_->SetSecurity(Service::kCryptoAes, false, false);
|
|
EXPECT_GT(service2_->SecurityLevel(), service_->SecurityLevel());
|
|
|
|
// For same encryption, prefer key rotation.
|
|
service_->SetSecurity(Service::kCryptoRc4, false, false);
|
|
service2_->SetSecurity(Service::kCryptoRc4, true, false);
|
|
EXPECT_GT(service2_->SecurityLevel(), service_->SecurityLevel());
|
|
|
|
// For same encryption, prefer authenticated AP.
|
|
service_->SetSecurity(Service::kCryptoRc4, false, false);
|
|
service2_->SetSecurity(Service::kCryptoRc4, false, true);
|
|
EXPECT_GT(service2_->SecurityLevel(), service_->SecurityLevel());
|
|
}
|
|
|
|
TEST_F(ServiceTest, SetErrorDetails) {
|
|
EXPECT_EQ(Service::kErrorDetailsNone, service_->error_details());
|
|
static const char kDetails[] = "Certificate revoked.";
|
|
ServiceMockAdaptor* adaptor = GetAdaptor();
|
|
EXPECT_CALL(*adaptor, EmitStringChanged(kErrorDetailsProperty, kDetails));
|
|
service_->SetErrorDetails(Service::kErrorDetailsNone);
|
|
EXPECT_EQ(Service::kErrorDetailsNone, service_->error_details());
|
|
service_->SetErrorDetails(kDetails);
|
|
EXPECT_EQ(kDetails, service_->error_details());
|
|
service_->SetErrorDetails(kDetails);
|
|
}
|
|
|
|
TEST_F(ServiceTest, SetAutoConnectFull) {
|
|
EXPECT_FALSE(service_->auto_connect());
|
|
Error error;
|
|
EXPECT_FALSE(GetAutoConnect(&error));
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
|
|
// false -> false
|
|
EXPECT_FALSE(service_->retain_auto_connect());
|
|
EXPECT_CALL(mock_manager_, UpdateService(_)).Times(0);
|
|
SetAutoConnectFull(false, &error);
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
EXPECT_FALSE(service_->auto_connect());
|
|
EXPECT_TRUE(service_->retain_auto_connect());
|
|
EXPECT_FALSE(GetAutoConnect(nullptr));
|
|
Mock::VerifyAndClearExpectations(&mock_manager_);
|
|
|
|
// Clear the |retain_auto_connect_| flag for the next test.
|
|
service_->Unload();
|
|
ASSERT_FALSE(service_->retain_auto_connect());
|
|
|
|
// false -> true
|
|
EXPECT_CALL(mock_manager_, UpdateService(_)).Times(1);
|
|
SetAutoConnectFull(true, &error);
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
EXPECT_TRUE(service_->auto_connect());
|
|
EXPECT_TRUE(GetAutoConnect(nullptr));
|
|
EXPECT_TRUE(service_->retain_auto_connect());
|
|
Mock::VerifyAndClearExpectations(&mock_manager_);
|
|
|
|
// Clear the |retain_auto_connect_| flag for the next test.
|
|
service_->Unload();
|
|
ASSERT_FALSE(service_->retain_auto_connect());
|
|
|
|
// true -> true
|
|
service_->SetAutoConnect(true);
|
|
EXPECT_CALL(mock_manager_, UpdateService(_)).Times(0);
|
|
SetAutoConnectFull(true, &error);
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
EXPECT_TRUE(service_->auto_connect());
|
|
EXPECT_TRUE(GetAutoConnect(nullptr));
|
|
EXPECT_TRUE(service_->retain_auto_connect());
|
|
Mock::VerifyAndClearExpectations(&mock_manager_);
|
|
|
|
// Clear the |retain_auto_connect_| flag for the next test.
|
|
service_->Unload();
|
|
ASSERT_FALSE(service_->retain_auto_connect());
|
|
|
|
// true -> false
|
|
service_->SetAutoConnect(true);
|
|
EXPECT_CALL(mock_manager_, UpdateService(_)).Times(1);
|
|
SetAutoConnectFull(false, &error);
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
EXPECT_FALSE(service_->auto_connect());
|
|
EXPECT_FALSE(GetAutoConnect(nullptr));
|
|
EXPECT_TRUE(service_->retain_auto_connect());
|
|
Mock::VerifyAndClearExpectations(&mock_manager_);
|
|
}
|
|
|
|
TEST_F(ServiceTest, SetAutoConnectFullUserUpdatePersists) {
|
|
// If the user sets the kAutoConnectProperty explicitly, the preference must
|
|
// be persisted, even if the property was not changed.
|
|
Error error;
|
|
MockProfileRefPtr mock_profile(
|
|
new MockProfile(control_interface(), metrics(), &mock_manager_));
|
|
NiceMock<MockStore> storage;
|
|
service_->set_profile(mock_profile);
|
|
service_->SetAutoConnect(true);
|
|
|
|
EXPECT_CALL(*mock_profile, UpdateService(_));
|
|
EXPECT_CALL(*mock_profile, GetConstStorage())
|
|
.WillOnce(Return(&storage));
|
|
EXPECT_CALL(mock_manager_, IsServiceEphemeral(IsRefPtrTo(service_)))
|
|
.WillOnce(Return(false));
|
|
EXPECT_FALSE(service_->retain_auto_connect());
|
|
SetAutoConnectFull(true, &error);
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
EXPECT_TRUE(service_->auto_connect());
|
|
EXPECT_TRUE(service_->retain_auto_connect());
|
|
}
|
|
|
|
TEST_F(ServiceTest, ClearAutoConnect) {
|
|
EXPECT_FALSE(service_->auto_connect());
|
|
Error error;
|
|
EXPECT_FALSE(GetAutoConnect(&error));
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
|
|
// unset -> false
|
|
EXPECT_FALSE(service_->retain_auto_connect());
|
|
EXPECT_CALL(mock_manager_, UpdateService(_)).Times(0);
|
|
ClearAutoConnect(&error);
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
EXPECT_FALSE(service_->retain_auto_connect());
|
|
EXPECT_FALSE(GetAutoConnect(nullptr));
|
|
Mock::VerifyAndClearExpectations(&mock_manager_);
|
|
|
|
// false -> false
|
|
SetAutoConnectFull(false, &error);
|
|
EXPECT_FALSE(GetAutoConnect(nullptr));
|
|
EXPECT_TRUE(service_->retain_auto_connect());
|
|
EXPECT_CALL(mock_manager_, UpdateService(_)).Times(0);
|
|
ClearAutoConnect(&error);
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
EXPECT_FALSE(service_->retain_auto_connect());
|
|
EXPECT_FALSE(GetAutoConnect(nullptr));
|
|
Mock::VerifyAndClearExpectations(&mock_manager_);
|
|
|
|
// true -> false
|
|
SetAutoConnectFull(true, &error);
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
EXPECT_TRUE(GetAutoConnect(nullptr));
|
|
EXPECT_CALL(mock_manager_, UpdateService(_)).Times(1);
|
|
ClearAutoConnect(&error);
|
|
EXPECT_FALSE(service_->retain_auto_connect());
|
|
EXPECT_FALSE(GetAutoConnect(nullptr));
|
|
Mock::VerifyAndClearExpectations(&mock_manager_);
|
|
}
|
|
|
|
TEST_F(ServiceTest, UniqueAttributes) {
|
|
EXPECT_NE(service_->serial_number_, service2_->serial_number_);
|
|
EXPECT_NE(service_->unique_name(), service2_->unique_name());
|
|
}
|
|
|
|
TEST_F(ServiceTest, PropertyChanges) {
|
|
TestCommonPropertyChanges(service_, GetAdaptor());
|
|
TestAutoConnectPropertyChange(service_, GetAdaptor());
|
|
}
|
|
|
|
// Custom property setters should return false, and make no changes, if
|
|
// the new value is the same as the old value.
|
|
TEST_F(ServiceTest, CustomSetterNoopChange) {
|
|
TestCustomSetterNoopChange(service_, &mock_manager_);
|
|
}
|
|
|
|
TEST_F(ServiceTest, GetTethering) {
|
|
Error error;
|
|
EXPECT_EQ("", service_->GetTethering(&error));
|
|
EXPECT_EQ(Error::kNotSupported, error.type());
|
|
}
|
|
|
|
class ServiceWithMockOnPropertyChanged : public ServiceUnderTest {
|
|
public:
|
|
ServiceWithMockOnPropertyChanged(ControlInterface* control_interface,
|
|
EventDispatcher* dispatcher,
|
|
Metrics* metrics,
|
|
Manager* manager)
|
|
: ServiceUnderTest(control_interface, dispatcher, metrics, manager) {}
|
|
MOCK_METHOD1(OnPropertyChanged, void(const string& property));
|
|
};
|
|
|
|
TEST_F(ServiceTest, ConfigureServiceTriggersOnPropertyChanged) {
|
|
auto service(make_scoped_refptr(
|
|
new ServiceWithMockOnPropertyChanged(control_interface(),
|
|
dispatcher(),
|
|
metrics(),
|
|
&mock_manager_)));
|
|
KeyValueStore args;
|
|
args.SetString(kUIDataProperty, "terpsichorean ejectamenta");
|
|
args.SetBool(kSaveCredentialsProperty, false);
|
|
|
|
// Calling Configure with different values from before triggers a single
|
|
// OnPropertyChanged call per property.
|
|
EXPECT_CALL(*service, OnPropertyChanged(kUIDataProperty)).Times(1);
|
|
EXPECT_CALL(*service, OnPropertyChanged(kSaveCredentialsProperty)).Times(1);
|
|
{
|
|
Error error;
|
|
service->Configure(args, &error);
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
}
|
|
Mock::VerifyAndClearExpectations(service.get());
|
|
|
|
// Calling Configure with the same values as before should not trigger
|
|
// OnPropertyChanged().
|
|
EXPECT_CALL(*service, OnPropertyChanged(_)).Times(0);
|
|
{
|
|
Error error;
|
|
service->Configure(args, &error);
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
}
|
|
}
|
|
|
|
TEST_F(ServiceTest, ClearExplicitlyDisconnected) {
|
|
EXPECT_FALSE(GetExplicitlyDisconnected());
|
|
EXPECT_CALL(mock_manager_, UpdateService(_)).Times(0);
|
|
service_->ClearExplicitlyDisconnected();
|
|
Mock::VerifyAndClearExpectations(&mock_manager_);
|
|
|
|
SetExplicitlyDisconnected(true);
|
|
EXPECT_CALL(mock_manager_, UpdateService(IsRefPtrTo(service_)));
|
|
service_->ClearExplicitlyDisconnected();
|
|
Mock::VerifyAndClearExpectations(&mock_manager_);
|
|
EXPECT_FALSE(GetExplicitlyDisconnected());
|
|
}
|
|
|
|
TEST_F(ServiceTest, Compare) {
|
|
// Construct our Services so that the string comparison of
|
|
// unique_name_ differs from the numerical comparison of
|
|
// serial_number_.
|
|
vector<scoped_refptr<MockService>> mock_services;
|
|
for (size_t i = 0; i < 11; ++i) {
|
|
mock_services.push_back(
|
|
new NiceMock<MockService>(control_interface(),
|
|
dispatcher(),
|
|
metrics(),
|
|
manager()));
|
|
}
|
|
scoped_refptr<MockService> service2 = mock_services[2];
|
|
scoped_refptr<MockService> service10 = mock_services[10];
|
|
mock_services.clear();
|
|
|
|
// Services should already be sorted by |serial_number_|.
|
|
EXPECT_TRUE(DefaultSortingOrderIs(service2, service10));
|
|
|
|
// Two otherwise equal services should be reordered by strength
|
|
service10->SetStrength(1);
|
|
EXPECT_TRUE(DefaultSortingOrderIs(service10, service2));
|
|
|
|
scoped_refptr<MockProfile> profile2(
|
|
new MockProfile(control_interface(), metrics(), manager(), ""));
|
|
scoped_refptr<MockProfile> profile10(
|
|
new MockProfile(control_interface(), metrics(), manager(), ""));
|
|
|
|
service2->set_profile(profile2);
|
|
service10->set_profile(profile10);
|
|
|
|
// When comparing two services with different profiles, prefer the one
|
|
// that is not ephemeral.
|
|
EXPECT_CALL(mock_manager_, IsServiceEphemeral(IsRefPtrTo(service2)))
|
|
.WillRepeatedly(Return(false));
|
|
EXPECT_CALL(mock_manager_, IsServiceEphemeral(IsRefPtrTo(service10)))
|
|
.WillRepeatedly(Return(true));
|
|
EXPECT_TRUE(DefaultSortingOrderIs(service2, service10));
|
|
Mock::VerifyAndClearExpectations(&mock_manager_);
|
|
|
|
// Prefer the service with the more recently applied profile if neither
|
|
// service is ephemeral.
|
|
EXPECT_CALL(mock_manager_, IsServiceEphemeral(_))
|
|
.WillRepeatedly(Return(false));
|
|
EXPECT_CALL(mock_manager_, IsProfileBefore(IsRefPtrTo(profile2),
|
|
IsRefPtrTo(profile10)))
|
|
.WillRepeatedly(Return(true));
|
|
EXPECT_CALL(mock_manager_, IsProfileBefore(IsRefPtrTo(profile10),
|
|
IsRefPtrTo(profile2)))
|
|
.WillRepeatedly(Return(false));
|
|
EXPECT_TRUE(DefaultSortingOrderIs(service10, service2));
|
|
|
|
// Security.
|
|
service2->SetSecurity(Service::kCryptoAes, true, true);
|
|
EXPECT_TRUE(DefaultSortingOrderIs(service2, service10));
|
|
|
|
// PriorityWithinTechnology.
|
|
service10->SetPriorityWithinTechnology(1, nullptr);
|
|
EXPECT_TRUE(DefaultSortingOrderIs(service10, service2));
|
|
service2->SetPriorityWithinTechnology(2, nullptr);
|
|
EXPECT_TRUE(DefaultSortingOrderIs(service2, service10));
|
|
|
|
// Technology.
|
|
EXPECT_CALL(*service2.get(), technology())
|
|
.WillRepeatedly(Return((Technology::kWifi)));
|
|
EXPECT_CALL(*service10.get(), technology())
|
|
.WillRepeatedly(Return(Technology::kEthernet));
|
|
|
|
technology_order_for_sorting_ = {Technology::kEthernet, Technology::kWifi};
|
|
EXPECT_TRUE(DefaultSortingOrderIs(service10, service2));
|
|
|
|
technology_order_for_sorting_ = {Technology::kWifi, Technology::kEthernet};
|
|
EXPECT_TRUE(DefaultSortingOrderIs(service2, service10));
|
|
|
|
// Priority.
|
|
service2->SetPriority(1, nullptr);
|
|
EXPECT_TRUE(DefaultSortingOrderIs(service2, service10));
|
|
service10->SetPriority(2, nullptr);
|
|
EXPECT_TRUE(DefaultSortingOrderIs(service10, service2));
|
|
|
|
// A service that has been connected before should be considered
|
|
// above a service that neither been connected to before nor has
|
|
// has managed credentials.
|
|
service2->has_ever_connected_ = true;
|
|
EXPECT_TRUE(DefaultSortingOrderIs(service2, service10));
|
|
|
|
// If one service has been connected to before, and the other is managed
|
|
// by Chrome they should rank same, so the priority will be considered
|
|
// instead.
|
|
service10->managed_credentials_ = true;
|
|
EXPECT_TRUE(DefaultSortingOrderIs(service10, service2));
|
|
service2->SetPriority(3, nullptr);
|
|
EXPECT_TRUE(DefaultSortingOrderIs(service2, service10));
|
|
|
|
// A service with managed credentials should be considered above one that
|
|
// has neither been connected to before nor has managed credentials.
|
|
service2->has_ever_connected_ = false;
|
|
EXPECT_TRUE(DefaultSortingOrderIs(service10, service2));
|
|
|
|
// Auto-connect.
|
|
service2->SetAutoConnect(true);
|
|
service10->SetAutoConnect(false);
|
|
EXPECT_TRUE(DefaultSortingOrderIs(service2, service10));
|
|
|
|
// Test is-dependent-on.
|
|
EXPECT_CALL(*service10.get(),
|
|
IsDependentOn(IsRefPtrTo(service2.get())))
|
|
.WillOnce(Return(true))
|
|
.WillOnce(Return(false));
|
|
EXPECT_TRUE(DefaultSortingOrderIs(service10, service2));
|
|
EXPECT_TRUE(DefaultSortingOrderIs(service2, service10));
|
|
|
|
// It doesn't make sense to have is-dependent-on ranking comparison in any of
|
|
// the remaining subtests below. Reset to the default.
|
|
EXPECT_CALL(*service10.get(), IsDependentOn(_)).WillRepeatedly(Return(false));
|
|
EXPECT_TRUE(DefaultSortingOrderIs(service2, service10));
|
|
|
|
// Connectable.
|
|
service10->SetConnectable(true);
|
|
service2->SetConnectable(false);
|
|
EXPECT_TRUE(DefaultSortingOrderIs(service10, service2));
|
|
|
|
// IsFailed.
|
|
EXPECT_CALL(*service2.get(), state())
|
|
.WillRepeatedly(Return(Service::kStateIdle));
|
|
EXPECT_CALL(*service2.get(), IsFailed())
|
|
.WillRepeatedly(Return(false));
|
|
EXPECT_CALL(*service10.get(), state())
|
|
.WillRepeatedly(Return(Service::kStateFailure));
|
|
EXPECT_CALL(*service10.get(), IsFailed())
|
|
.WillRepeatedly(Return(true));
|
|
EXPECT_TRUE(DefaultSortingOrderIs(service2, service10));
|
|
|
|
// Connecting.
|
|
EXPECT_CALL(*service10.get(), state())
|
|
.WillRepeatedly(Return(Service::kStateAssociating));
|
|
EXPECT_CALL(*service10.get(), IsConnecting())
|
|
.WillRepeatedly(Return(true));
|
|
EXPECT_TRUE(DefaultSortingOrderIs(service10, service2));
|
|
|
|
// Connected-but-portalled preferred over unconnected.
|
|
EXPECT_CALL(*service2.get(), state())
|
|
.WillRepeatedly(Return(Service::kStatePortal));
|
|
EXPECT_CALL(*service2.get(), IsConnected())
|
|
.WillRepeatedly(Return(true));
|
|
EXPECT_TRUE(DefaultSortingOrderIs(service2, service10));
|
|
|
|
// Connected preferred over connected-but-portalled.
|
|
service10->SetConnectable(false);
|
|
service2->SetConnectable(true);
|
|
EXPECT_CALL(*service10.get(), state())
|
|
.WillRepeatedly(Return(Service::kStateConnected));
|
|
EXPECT_CALL(*service10.get(), IsConnected())
|
|
.WillRepeatedly(Return(true));
|
|
EXPECT_TRUE(DefaultSortingOrderIs(service10, service2));
|
|
|
|
// Online preferred over just connected.
|
|
EXPECT_CALL(*service2.get(), state())
|
|
.WillRepeatedly(Return(Service::kStateOnline));
|
|
EXPECT_TRUE(DefaultSortingOrderIs(service10, service2));
|
|
|
|
// Connectivity state ignored if this is specified.
|
|
const bool kDoNotCompareConnectivityState = false;
|
|
EXPECT_TRUE(SortingOrderIs(service2, service10,
|
|
kDoNotCompareConnectivityState));
|
|
}
|
|
|
|
} // namespace shill
|