blob: eb91aaff1b6eaa618db84f83b6f954aab0ddd7d3 [file] [log] [blame]
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "shill/service.h"
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <base/bind.h>
#include <base/memory/scoped_refptr.h>
#include <chromeos/dbus/service_constants.h>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "shill/dhcp/mock_dhcp_properties.h"
#include "shill/ethernet/ethernet_service.h"
#include "shill/event_dispatcher.h"
#include "shill/fake_store.h"
#include "shill/ipconfig.h"
#include "shill/manager.h"
#include "shill/mock_adaptors.h"
#include "shill/mock_connection.h"
#include "shill/mock_device_info.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/net/mock_time.h"
#include "shill/property_store_test.h"
#include "shill/service_property_change_test.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::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::SetArgPointee;
using testing::StrictMock;
using testing::Test;
using testing::Values;
namespace {
const char kConnectDisconnectReason[] = "RPC";
const char kGUID[] = "guid";
} // namespace
namespace shill {
class ServiceTest : public PropertyStoreTest {
public:
ServiceTest()
: mock_manager_(control_interface(), dispatcher(), metrics()),
service_(new ServiceUnderTest(&mock_manager_)),
service2_(new ServiceUnderTest(&mock_manager_)),
storage_id_(ServiceUnderTest::kStorageId),
power_manager_(new MockPowerManager(control_interface())) {
ON_CALL(*control_interface(), CreatePowerManagerProxy(_, _, _))
.WillByDefault(ReturnNull());
service_->disconnects_.time_ = &time_;
service_->misconnects_.time_ = &time_;
DefaultValue<Timestamp>::Set(Timestamp());
#if !defined(DISABLE_WIFI) || !defined(DISABLE_WIRED_8021X)
service_->eap_.reset(new NiceMock<MockEapCredentials>());
#endif // DISABLE_WIFI || DISABLE_WIRED_8021X
mock_manager_.running_ = true;
mock_manager_.set_power_manager(power_manager_); // Passes ownership.
}
~ServiceTest() override = default;
MOCK_METHOD(void, TestCallback, (const Error&));
protected:
using MockProfileRefPtr = scoped_refptr<MockProfile>;
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 NoteFailureEvent() { service_->NoteFailureEvent(); }
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);
}
const base::CancelableClosure& GetPendingConnectTask() {
return service_->pending_connect_task_;
}
bool HasPendingConnect() { return !GetPendingConnectTask().IsCancelled(); }
bool SortingOrderIs(const ServiceRefPtr& service0,
const ServiceRefPtr& service1,
bool should_compare_connectivity_state) {
return Service::Compare(service0, service1,
should_compare_connectivity_state,
technology_order_for_sorting_)
.first;
}
bool DefaultSortingOrderIs(const ServiceRefPtr& service0,
const ServiceRefPtr& service1) {
const bool kShouldCompareConnectivityState = true;
return SortingOrderIs(service0, service1, kShouldCompareConnectivityState);
}
NiceMock<MockManager> mock_manager_;
NiceMock<MockTime> time_;
scoped_refptr<ServiceUnderTest> service_;
scoped_refptr<ServiceUnderTest> service2_;
string storage_id_;
MockPowerManager* power_manager_; // Owned by |mock_manager_|.
vector<Technology> technology_order_for_sorting_;
};
class AllMockServiceTest : public testing::Test {
public:
AllMockServiceTest()
: manager_(&control_interface_, &dispatcher_, &metrics_),
service_(new ServiceUnderTest(&manager_)) {}
~AllMockServiceTest() override = default;
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>(), true);
}
{
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>(),
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) {
FakeStore storage;
EXPECT_EQ("", service_->GetLoadableStorageIdentifier(storage));
// Setting any property will add an entry for |storage_id_|.
storage.SetString(storage_id_, Service::kStorageGUID, kGUID);
EXPECT_EQ(storage_id_, service_->GetLoadableStorageIdentifier(storage));
}
TEST_F(ServiceTest, IsLoadableFrom) {
FakeStore storage;
EXPECT_FALSE(service_->IsLoadableFrom(storage));
// Setting any property will add an entry for |storage_id_|.
storage.SetString(storage_id_, Service::kStorageGUID, kGUID);
EXPECT_TRUE(service_->IsLoadableFrom(storage));
}
#if !defined(DISABLE_WIFI) || !defined(DISABLE_WIRED_8021X)
class ServiceWithOnEapCredentialsChangedOverride : public ServiceUnderTest {
public:
ServiceWithOnEapCredentialsChangedOverride(Manager* manager,
EapCredentials* eap)
: ServiceUnderTest(manager) {
SetEapCredentials(eap);
}
void OnEapCredentialsChanged(Service::UpdateCredentialsReason) override {
SetHasEverConnected(false);
}
};
#endif // DISABLE_WIFI || DISABLE_WIRED_8021X
TEST_F(ServiceTest, LoadTrafficCounters) {
FakeStore storage;
const uint64_t kUserRxBytes = 1234;
const uint64_t kChromeTxPackets = 9876;
vector<uint64_t> kUserCounters{kUserRxBytes, 0, 0, 0};
vector<uint64_t> kChromeCounters{0, 0, 0, kChromeTxPackets};
storage.SetUint64(storage_id_,
Service::GetCurrentTrafficCounterKey(
patchpanel::TrafficCounter::USER,
Service::kStorageTrafficCounterRxBytesSuffix),
kUserRxBytes);
storage.SetUint64(storage_id_,
Service::GetCurrentTrafficCounterKey(
patchpanel::TrafficCounter::CHROME,
Service::kStorageTrafficCounterTxPacketsSuffix),
kChromeTxPackets);
EXPECT_TRUE(service_->Load(&storage));
EXPECT_EQ(service_->current_traffic_counters_.size(), 2);
for (size_t i = 0; i < Service::kTrafficCounterArraySize; i++) {
EXPECT_EQ(
service_
->current_traffic_counters_[patchpanel::TrafficCounter::USER][i],
kUserCounters[i]);
EXPECT_EQ(
service_
->current_traffic_counters_[patchpanel::TrafficCounter::CHROME][i],
kChromeCounters[i]);
}
}
TEST_F(ServiceTest, Load) {
FakeStore storage;
const string kCheckPortal("check-portal");
const int kPriority = 20;
const string kProxyConfig("proxy-config");
const string kUIData("ui-data");
storage.SetString(storage_id_, Service::kStorageCheckPortal, kCheckPortal);
storage.SetString(storage_id_, Service::kStorageGUID, kGUID);
storage.SetBool(storage_id_, Service::kStorageHasEverConnected, true);
storage.SetInt(storage_id_, Service::kStoragePriority, kPriority);
storage.SetString(storage_id_, Service::kStorageProxyConfig, kProxyConfig);
storage.SetString(storage_id_, Service::kStorageUIData, kUIData);
auto* dhcp_props = new DhcpProperties(&mock_manager_);
service_->dhcp_properties_.reset(dhcp_props);
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_);
// Removing the storage entry should cause the service to fail to load.
storage.DeleteGroup(storage_id_);
EXPECT_FALSE(service_->Load(&storage));
// Set an empty GUID to add a storage entry so that the service loads.
storage.SetString(storage_id_, Service::kStorageGUID, "");
// Assure that parameters are set to default if not available in the profile.
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_);
}
#if !defined(DISABLE_WIFI) || !defined(DISABLE_WIRED_8021X)
TEST_F(ServiceTest, LoadEap) {
FakeStore storage;
const char kIdentity[] = "identity";
storage.SetString(storage_id_, EapCredentials::kStorageCredentialEapIdentity,
kIdentity);
auto* eap = new EapCredentials();
ASSERT_FALSE(service2_->eap());
service2_->SetEapCredentials(eap);
service2_->SetHasEverConnected(true);
EXPECT_TRUE(service2_->has_ever_connected());
EXPECT_TRUE(service2_->Load(&storage));
EXPECT_EQ(service2_->eap()->identity(), kIdentity);
std::string identity;
EXPECT_TRUE(storage.GetString(
storage_id_, EapCredentials::kStorageCredentialEapIdentity, &identity));
EXPECT_EQ(identity, kIdentity);
// has_ever_connected_ is unaffected when loading eap credentials.
EXPECT_TRUE(service2_->has_ever_connected());
}
#endif
TEST_F(ServiceTest, LoadFail) {
FakeStore storage;
EXPECT_FALSE(service_->Load(&storage));
}
TEST_F(ServiceTest, LoadAutoConnect) {
FakeStore storage;
storage.SetString(storage_id_, Service::kStorageGUID, kGUID);
// AutoConnect is unset.
EXPECT_TRUE(service_->Load(&storage));
EXPECT_FALSE(service_->auto_connect());
EXPECT_FALSE(service_->retain_auto_connect());
// AutoConnect is false.
storage.SetBool(storage_id_, Service::kStorageAutoConnect, false);
EXPECT_TRUE(service_->Load(&storage));
EXPECT_FALSE(service_->auto_connect());
EXPECT_TRUE(service_->retain_auto_connect());
// AutoConnect is true.
storage.SetBool(storage_id_, Service::kStorageAutoConnect, true);
EXPECT_TRUE(service_->Load(&storage));
EXPECT_TRUE(service_->auto_connect());
EXPECT_TRUE(service_->retain_auto_connect());
}
TEST_F(ServiceTest, SaveString) {
FakeStore storage;
static const char kKey[] = "test-key";
static const char kData[] = "test-data";
// Test setting kKey to a value.
service_->SaveStringOrClear(&storage, storage_id_, kKey, kData);
std::string data;
EXPECT_TRUE(storage.GetString(storage_id_, kKey, &data));
EXPECT_EQ(data, kData);
// Setting kKey to an empty value should delete the entry
service_->SaveStringOrClear(&storage, storage_id_, kKey, "");
EXPECT_FALSE(storage.GetString(storage_id_, kKey, &data));
}
TEST_F(ServiceTest, SaveTrafficCounters) {
FakeStore storage;
std::valarray<uint64_t> kVPNCounters{0, 19, 28, 0};
std::valarray<uint64_t> kUnknownCounters{333, 222, 555, 888};
service_->current_traffic_counters_[patchpanel::TrafficCounter::VPN] =
kVPNCounters;
EXPECT_TRUE(service_->Save(&storage));
vector<uint64_t> kActualVPNCounters(Service::kTrafficCounterArraySize);
storage.GetUint64(storage_id_,
Service::GetCurrentTrafficCounterKey(
patchpanel::TrafficCounter::VPN,
Service::kStorageTrafficCounterRxBytesSuffix),
&kActualVPNCounters[0]);
storage.GetUint64(storage_id_,
Service::GetCurrentTrafficCounterKey(
patchpanel::TrafficCounter::VPN,
Service::kStorageTrafficCounterTxBytesSuffix),
&kActualVPNCounters[1]);
storage.GetUint64(storage_id_,
Service::GetCurrentTrafficCounterKey(
patchpanel::TrafficCounter::VPN,
Service::kStorageTrafficCounterRxPacketsSuffix),
&kActualVPNCounters[2]);
storage.GetUint64(storage_id_,
Service::GetCurrentTrafficCounterKey(
patchpanel::TrafficCounter::VPN,
Service::kStorageTrafficCounterTxPacketsSuffix),
&kActualVPNCounters[3]);
for (size_t i = 0; i < Service::kTrafficCounterArraySize; i++) {
EXPECT_EQ(kVPNCounters[i], kActualVPNCounters[i]);
}
}
TEST_F(ServiceTest, Save) {
FakeStore storage;
service_->technology_ = Technology::kWifi;
EXPECT_TRUE(service_->Save(&storage));
std::string type;
EXPECT_TRUE(storage.GetString(storage_id_, Service::kStorageType, &type));
EXPECT_EQ(type, service_->GetTechnologyString());
}
#if !defined(DISABLE_WIFI) || !defined(DISABLE_WIRED_8021X)
TEST_F(ServiceTest, SaveEap) {
FakeStore storage;
const char kIdentity[] = "identity";
auto* eap = new EapCredentials();
eap->set_identity(kIdentity);
ASSERT_FALSE(service2_->eap());
service2_->SetEapCredentials(eap);
service2_->set_save_credentials(true);
EXPECT_TRUE(service2_->Save(&storage));
std::string identity;
EXPECT_TRUE(storage.GetString(
storage_id_, EapCredentials::kStorageCredentialEapIdentity, &identity));
EXPECT_EQ(identity, kIdentity);
}
#endif
TEST_F(ServiceTest, SaveDhcpProperties) {
FakeStore storage;
const char kHostname[] = "hostname";
service_->dhcp_properties_for_testing()
->properties_for_testing()
->Set<std::string>(DhcpProperties::kHostnameProperty, kHostname);
EXPECT_TRUE(service_->Save(&storage));
std::string key = std::string(DhcpProperties::kPropertyPrefix) +
DhcpProperties::kHostnameProperty;
std::string hostname;
EXPECT_TRUE(storage.GetString(storage_id_, key, &hostname));
EXPECT_EQ(hostname, kHostname);
}
TEST_F(ServiceTest, RetainAutoConnect) {
FakeStore storage;
// AutoConnect flag set true.
service_->EnableAndRetainAutoConnect();
EXPECT_TRUE(service_->Save(&storage));
bool auto_connect = false;
EXPECT_TRUE(storage.GetBool(storage_id_, Service::kStorageAutoConnect,
&auto_connect));
EXPECT_TRUE(auto_connect);
// AutoConnect flag set false.
service_->SetAutoConnect(false);
EXPECT_TRUE(service_->Save(&storage));
EXPECT_TRUE(storage.GetBool(storage_id_, Service::kStorageAutoConnect,
&auto_connect));
EXPECT_FALSE(auto_connect);
}
TEST_F(ServiceTest, HasEverConnectedSavedToProfile) {
FakeStore storage;
// HasEverConnected flag set true.
service_->SetHasEverConnected(true);
EXPECT_TRUE(service_->Save(&storage));
bool has_ever_connected = false;
EXPECT_TRUE(storage.GetBool(storage_id_, Service::kStorageHasEverConnected,
&has_ever_connected));
EXPECT_TRUE(has_ever_connected);
// HasEverConnected flag set false.
service_->SetHasEverConnected(false);
EXPECT_TRUE(service_->Save(&storage));
EXPECT_TRUE(storage.GetBool(storage_id_, Service::kStorageHasEverConnected,
&has_ever_connected));
EXPECT_FALSE(has_ever_connected);
}
TEST_F(ServiceTest, Unload) {
FakeStore storage;
storage.SetString(storage_id_, Service::kStorageGUID, kGUID);
storage.SetBool(storage_id_, Service::kStorageHasEverConnected, true);
EXPECT_FALSE(service_->explicitly_disconnected_);
EXPECT_FALSE(service_->has_ever_connected_);
service_->explicitly_disconnected_ = true;
EXPECT_TRUE(service_->Load(&storage));
EXPECT_EQ(kGUID, service_->guid_);
EXPECT_FALSE(service_->explicitly_disconnected_);
EXPECT_TRUE(service_->has_ever_connected_);
service_->explicitly_disconnected_ = true;
service_->Unload();
EXPECT_EQ(string(""), service_->guid_);
EXPECT_FALSE(service_->explicitly_disconnected_);
EXPECT_FALSE(service_->has_ever_connected_);
}
// Tests that static IP configs are set, stored and unloaded correctly.
TEST_F(ServiceTest, StaticIPConfigs) {
const char kTestIpAddress[] = "1.2.3.4";
const int32_t kTestPrefixlen = 5;
EXPECT_FALSE(service_->HasStaticIPAddress());
KeyValueStore static_ip_configs;
static_ip_configs.Set(kAddressProperty, std::string(kTestIpAddress));
static_ip_configs.Set(kPrefixlenProperty, kTestPrefixlen);
service_->mutable_store()->SetKeyValueStoreProperty(
kStaticIPConfigProperty, static_ip_configs, /*error=*/nullptr);
EXPECT_TRUE(service_->HasStaticIPAddress());
FakeStore storage;
ASSERT_TRUE(service_->Save(&storage));
service_->Unload();
EXPECT_FALSE(service_->HasStaticIPAddress());
ASSERT_TRUE(service_->Load(&storage));
EXPECT_TRUE(service_->HasStaticIPAddress());
}
TEST_F(ServiceTest, State) {
EXPECT_EQ(Service::kStateIdle, service_->state());
EXPECT_EQ(Service::kStateIdle, GetPreviousState());
EXPECT_EQ(Service::kFailureNone, service_->failure());
const string no_error(Service::ConnectFailureToString(Service::kFailureNone));
EXPECT_EQ(no_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::kFailureNone, 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(no_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(&mock_manager_));
FakeStore 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) {
const int kStatusCode = 204;
EXPECT_CALL(*GetAdaptor(),
EmitStringChanged(kPortalDetectionFailedPhaseProperty,
kPortalDetectionPhaseDns))
.Times(1);
EXPECT_CALL(*GetAdaptor(),
EmitStringChanged(kPortalDetectionFailedStatusProperty,
kPortalDetectionStatusTimeout))
.Times(1);
EXPECT_CALL(
*GetAdaptor(),
EmitIntChanged(kPortalDetectionFailedStatusCodeProperty, kStatusCode))
.Times(1);
service_->SetPortalDetectionFailure(
kPortalDetectionPhaseDns, kPortalDetectionStatusTimeout, kStatusCode);
EXPECT_EQ(kPortalDetectionPhaseDns,
service_->portal_detection_failure_phase_);
EXPECT_EQ(kPortalDetectionStatusTimeout,
service_->portal_detection_failure_status_);
EXPECT_EQ(kStatusCode, service_->portal_detection_failure_status_code_);
}
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::kFailureNone, 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(kConnectDisconnectReason, &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(kConnectDisconnectReason, &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(kConnectDisconnectReason, &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(kConnectDisconnectReason, &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(kConnectDisconnectReason, &error);
EXPECT_CALL(*metrics(), NotifyUserInitiatedConnectionResult(_, _)).Times(0);
EXPECT_CALL(*metrics(), NotifyUserInitiatedConnectionFailureReason(_, _))
.Times(0);
service_->SetFailure(Service::kFailureDHCP);
Mock::VerifyAndClearExpectations(metrics());
}
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(kConnectDisconnectReason, &error);
EXPECT_FALSE(service_->IsAutoConnectable(&reason));
EXPECT_STREQ(Service::kAutoConnExplicitDisconnect, reason);
// But if the Service is reloaded, it is eligible for auto-connect
// again.
FakeStore storage;
storage.SetString(storage_id_, Service::kStorageGUID, kGUID);
EXPECT_TRUE(service_->Load(&storage));
EXPECT_TRUE(service_->IsAutoConnectable(&reason));
// A deliberate Connect should also re-enable auto-connect.
service_->UserInitiatedDisconnect(kConnectDisconnectReason, &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(kConnectDisconnectReason, &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::LOGGING_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_->GetMaxAutoConnectCooldownTimeMilliseconds()) {
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_->GetMaxAutoConnectCooldownTimeMilliseconds()));
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.Set<string>("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.Set<bool>(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.Set<string>(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.Set<Strings>(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 NiceMock<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.Set<string>(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.Set<int32_t>(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.Set<bool>(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.Set<string>(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.Set<bool>("key0", true);
KeyValueStore key_value_store1;
key_value_store1.Set<int32_t>("key1", 1);
service_->SetKeyValueStore(key_value_store0, nullptr);
ASSERT_EQ(key_value_store0, service_->GetKeyValueStore(nullptr));
KeyValueStore args;
args.Set<KeyValueStore>(ServiceUnderTest::kKeyValueStoreProperty,
key_value_store1);
Error error;
service_->Configure(args, &error);
EXPECT_TRUE(error.IsSuccess());
EXPECT_EQ(key_value_store1, service_->GetKeyValueStore(nullptr));
}
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.Set<bool>("key0", true);
KeyValueStore key_value_store1;
key_value_store1.Set<int32_t>("key1", 1);
service_->SetKeyValueStore(key_value_store0, nullptr);
{
KeyValueStore args;
args.Set<string>(kGuidProperty, kGUID0);
args.Set<bool>(kAutoConnectProperty, false);
args.Set<int32_t>(kPriorityProperty, kPriority0);
args.Set<Strings>(ServiceUnderTest::kStringsProperty, kStrings0);
args.Set<KeyValueStore>(ServiceUnderTest::kKeyValueStoreProperty,
key_value_store0);
EXPECT_TRUE(service_->DoPropertiesMatch(args));
}
{
KeyValueStore args;
args.Set<string>(kGuidProperty, kGUID1);
args.Set<bool>(kAutoConnectProperty, false);
args.Set<int32_t>(kPriorityProperty, kPriority0);
args.Set<Strings>(ServiceUnderTest::kStringsProperty, kStrings0);
args.Set<KeyValueStore>(ServiceUnderTest::kKeyValueStoreProperty,
key_value_store0);
EXPECT_FALSE(service_->DoPropertiesMatch(args));
}
{
KeyValueStore args;
args.Set<string>(kGuidProperty, kGUID0);
args.Set<bool>(kAutoConnectProperty, true);
args.Set<int32_t>(kPriorityProperty, kPriority0);
args.Set<Strings>(ServiceUnderTest::kStringsProperty, kStrings0);
args.Set<KeyValueStore>(ServiceUnderTest::kKeyValueStoreProperty,
key_value_store0);
EXPECT_FALSE(service_->DoPropertiesMatch(args));
}
{
KeyValueStore args;
args.Set<string>(kGuidProperty, kGUID0);
args.Set<bool>(kAutoConnectProperty, false);
args.Set<int32_t>(kPriorityProperty, kPriority1);
args.Set<Strings>(ServiceUnderTest::kStringsProperty, kStrings0);
args.Set<KeyValueStore>(ServiceUnderTest::kKeyValueStoreProperty,
key_value_store0);
EXPECT_FALSE(service_->DoPropertiesMatch(args));
}
{
KeyValueStore args;
args.Set<string>(kGuidProperty, kGUID0);
args.Set<bool>(kAutoConnectProperty, false);
args.Set<int32_t>(kPriorityProperty, kPriority0);
args.Set<Strings>(ServiceUnderTest::kStringsProperty, kStrings1);
args.Set<KeyValueStore>(ServiceUnderTest::kKeyValueStoreProperty,
key_value_store0);
EXPECT_FALSE(service_->DoPropertiesMatch(args));
}
{
KeyValueStore args;
args.Set<string>(kGuidProperty, kGUID0);
args.Set<bool>(kAutoConnectProperty, false);
args.Set<int32_t>(kPriorityProperty, kPriority0);
args.Set<Strings>(ServiceUnderTest::kStringsProperty, kStrings0);
args.Set<KeyValueStore>(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>(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, OnPropertyChanged) {
scoped_refptr<MockProfile> profile(new StrictMock<MockProfile>(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);
FakeStore 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::kStateNoConnectivity;
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::kStateNoConnectivity;
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) {
std::string default_friendly_name = service_->friendly_name_;
ServiceMockAdaptor* adaptor = GetAdaptor();
EXPECT_CALL(*adaptor, EmitStringChanged(_, _)).Times(0);
service_->SetFriendlyName(default_friendly_name);
EXPECT_EQ(default_friendly_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_TRUE(service_->connectable());
ServiceMockAdaptor* adaptor = GetAdaptor();
EXPECT_CALL(*adaptor, EmitBoolChanged(_, _)).Times(0);
EXPECT_CALL(mock_manager_, HasService(_)).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(_, _)).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(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_SUITE_P(WriteOnlyServicePropertyTestInstance,
WriteOnlyServicePropertyTest,
Values(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());
}
auto mock_device_info =
std::make_unique<NiceMock<MockDeviceInfo>>(&mock_manager_);
scoped_refptr<MockConnection> mock_connection(
new NiceMock<MockConnection>(mock_device_info.get()));
service_->connection_ = mock_connection;
{
Error error;
const RpcIdentifier empty_rpcid;
EXPECT_CALL(*mock_connection, ipconfig_rpc_identifier())
.WillOnce(ReturnRef(empty_rpcid));
EXPECT_EQ(control_interface()->NullRpcIdentifier(),
service_->GetIPConfigRpcIdentifier(&error));
EXPECT_EQ(Error::kNotFound, error.type());
}
{
Error error;
const RpcIdentifier nonempty_rpcid("/ipconfig/path");
EXPECT_CALL(*mock_connection, ipconfig_rpc_identifier())
.WillOnce(ReturnRef(nonempty_rpcid));
EXPECT_EQ(nonempty_rpcid, 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:
explicit ServiceWithMockOnEapCredentialsChanged(Manager* manager)
: ServiceUnderTest(manager), is_8021x_(false) {}
MOCK_METHOD(void,
OnEapCredentialsChanged,
(Service::UpdateCredentialsReason),
(override));
bool Is8021x() const override { 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(&mock_manager_));
static const char* const kEapCredentialProperties[] = {
kEapAnonymousIdentityProperty, kEapCertIdProperty,
kEapIdentityProperty, kEapKeyIdProperty,
kEapPasswordProperty, kEapPinProperty,
};
static const char* const kEapNonCredentialProperties[] = {
kEapCaCertIdProperty, 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 (auto eap_credential_property : kEapCredentialProperties) {
service->OnPropertyChanged(eap_credential_property);
}
for (auto eap_non_credential_property : kEapNonCredentialProperties) {
service->OnPropertyChanged(eap_non_credential_property);
}
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 (auto eap_credential_property : kEapCredentialProperties) {
EXPECT_CALL(*service,
OnEapCredentialsChanged(Service::kReasonPropertyUpdate))
.Times(1);
service->OnPropertyChanged(eap_credential_property);
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 (auto eap_non_credential_property : kEapNonCredentialProperties) {
service->OnPropertyChanged(eap_non_credential_property);
}
}
TEST_F(ServiceTest, Certification) {
EXPECT_TRUE(service_->remote_certification_.empty());
ScopedMockLog log;
EXPECT_CALL(
log, Log(logging::LOGGING_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::LOGGING_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::LOGGING_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, NoteFailureEventIdle) {
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, NoteFailureEventOnSetStateFailure) {
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, NoteFailureEventOnSetFailureSilent) {
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, NoteFailureEventNonEvent) {
EXPECT_CALL(time_, GetNow()).Times(0);
// Explicit disconnect is a non-event.
SetStateField(Service::kStateOnline);
SetExplicitlyDisconnected(true);
NoteFailureEvent();
EXPECT_TRUE(GetDisconnects()->Empty());
EXPECT_TRUE(GetMisconnects()->Empty());
// Failure to idle transition is a non-event.
SetStateField(Service::kStateFailure);
SetExplicitlyDisconnected(false);
NoteFailureEvent();
EXPECT_TRUE(GetDisconnects()->Empty());
EXPECT_TRUE(GetMisconnects()->Empty());
// Disconnect while manager is stopped is a non-event.
SetStateField(Service::kStateOnline);
SetManagerRunning(false);
NoteFailureEvent();
EXPECT_TRUE(GetDisconnects()->Empty());
EXPECT_TRUE(GetMisconnects()->Empty());
// Disconnect while suspending is a non-event.
SetManagerRunning(true);
SetSuspending(true);
NoteFailureEvent();
EXPECT_TRUE(GetDisconnects()->Empty());
EXPECT_TRUE(GetMisconnects()->Empty());
}
TEST_F(ServiceTest, NoteFailureEventDisconnectOnce) {
const int kNow = 5;
EXPECT_FALSE(service_->explicitly_disconnected());
SetStateField(Service::kStateOnline);
EXPECT_CALL(time_, GetNow()).WillOnce(Return(GetTimestamp(kNow, kNow, "")));
NoteFailureEvent();
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, NoteFailureEventMisconnectOnce) {
const int kNow = 7;
EXPECT_FALSE(service_->explicitly_disconnected());
SetStateField(Service::kStateConfiguring);
EXPECT_CALL(time_, GetNow()).WillOnce(Return(GetTimestamp(kNow, kNow, "")));
NoteFailureEvent();
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, NoteFailureEventDiscardOld) {
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, "")));
NoteFailureEvent();
ASSERT_EQ(1, events->Size());
EXPECT_EQ(now, events->Front().monotonic.tv_sec);
}
}
TEST_F(ServiceTest, NoteFailureEventDiscardExcessive) {
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()));
NoteFailureEvent();
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()));
NoteFailureEvent();
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(&mock_manager_));
FakeStore 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_);
}
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());
}
TEST_F(ServiceTest, MeteredOverride) {
Error error;
service_->SetMeteredProperty(true, &error);
EXPECT_TRUE(service_->IsMetered());
service_->SetMeteredProperty(false, &error);
EXPECT_FALSE(service_->IsMetered());
}
TEST_F(ServiceTest, SaveMeteredOverride) {
FakeStore storage;
EXPECT_TRUE(service_->Save(&storage));
// Newly created services should not have a metered override value
// since that is set by the user, and should thus have no value
// to save to the storage.
bool metered_override = false;
EXPECT_FALSE(storage.GetBool(storage_id_, Service::kStorageMeteredOverride,
&metered_override));
Error error;
service_->SetMeteredProperty(true, &error);
EXPECT_TRUE(service_->Save(&storage));
EXPECT_TRUE(storage.GetBool(storage_id_, Service::kStorageMeteredOverride,
&metered_override));
EXPECT_TRUE(metered_override);
}
TEST_F(ServiceTest, IsNotMeteredByDefault) {
EXPECT_FALSE(service_->IsMetered());
}
class ServiceWithMockOnPropertyChanged : public ServiceUnderTest {
public:
explicit ServiceWithMockOnPropertyChanged(Manager* manager)
: ServiceUnderTest(manager) {}
MOCK_METHOD(void, OnPropertyChanged, (const string&), (override));
};
TEST_F(ServiceTest, ConfigureServiceTriggersOnPropertyChanged) {
auto service(
base::MakeRefCounted<ServiceWithMockOnPropertyChanged>(&mock_manager_));
KeyValueStore args;
args.Set<string>(kUIDataProperty, "terpsichorean ejectamenta");
args.Set<bool>(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) {
vector<scoped_refptr<MockService>> mock_services;
for (size_t i = 0; i < 11; ++i) {
mock_services.push_back(new NiceMock<MockService>(&mock_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));
// A service that has been connected before should be considered
// above a service that has never been connected to before.
service2->has_ever_connected_ = true;
EXPECT_TRUE(DefaultSortingOrderIs(service2, service10));
scoped_refptr<MockProfile> profile2(new MockProfile(manager(), ""));
scoped_refptr<MockProfile> profile10(new MockProfile(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(true));
EXPECT_CALL(mock_manager_, IsServiceEphemeral(IsRefPtrTo(service10)))
.WillRepeatedly(Return(false));
EXPECT_TRUE(DefaultSortingOrderIs(service10, service2));
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(false));
EXPECT_CALL(mock_manager_,
IsProfileBefore(IsRefPtrTo(profile10), IsRefPtrTo(profile2)))
.WillRepeatedly(Return(true));
EXPECT_TRUE(DefaultSortingOrderIs(service2, service10));
// Security.
service10->SetSecurity(Service::kCryptoAes, true, true);
EXPECT_TRUE(DefaultSortingOrderIs(service10, service2));
// Auto-connect.
service2->SetAutoConnect(true);
EXPECT_TRUE(DefaultSortingOrderIs(service2, service10));
// Managed credentials
service10->managed_credentials_ = true;
EXPECT_TRUE(DefaultSortingOrderIs(service10, service2));
// Priority.
service2->SetPriority(1, nullptr);
EXPECT_TRUE(DefaultSortingOrderIs(service2, service10));
service10->SetPriority(2, nullptr);
EXPECT_TRUE(DefaultSortingOrderIs(service10, service2));
// Technology.
EXPECT_CALL(*service2, technology())
.WillRepeatedly(Return(Technology::kEthernet));
EXPECT_CALL(*service10, technology())
.WillRepeatedly(Return((Technology::kWifi)));
technology_order_for_sorting_ = {Technology::kEthernet, Technology::kWifi};
EXPECT_TRUE(DefaultSortingOrderIs(service2, service10));
// Connectable.
service10->SetConnectable(true);
service2->SetConnectable(false);
EXPECT_TRUE(DefaultSortingOrderIs(service10, service2));
// IsFailed.
EXPECT_CALL(*service2, state()).WillRepeatedly(Return(Service::kStateIdle));
EXPECT_CALL(*service2, IsFailed()).WillRepeatedly(Return(false));
EXPECT_CALL(*service10, state())
.WillRepeatedly(Return(Service::kStateFailure));
EXPECT_CALL(*service10, IsFailed()).WillRepeatedly(Return(true));
EXPECT_TRUE(DefaultSortingOrderIs(service2, service10));
// Connecting.
EXPECT_CALL(*service10, state())
.WillRepeatedly(Return(Service::kStateAssociating));
EXPECT_CALL(*service10, IsConnecting()).WillRepeatedly(Return(true));
EXPECT_TRUE(DefaultSortingOrderIs(service10, service2));
// Connected-but-portalled preferred over unconnected.
EXPECT_CALL(*service2, state())
.WillRepeatedly(Return(Service::kStateNoConnectivity));
EXPECT_CALL(*service2, IsConnected(nullptr)).WillRepeatedly(Return(true));
EXPECT_TRUE(DefaultSortingOrderIs(service2, service10));
// Connected preferred over connected-but-portalled.
service10->SetConnectable(false);
service2->SetConnectable(true);
EXPECT_CALL(*service10, state())
.WillRepeatedly(Return(Service::kStateConnected));
EXPECT_CALL(*service10, IsConnected(nullptr)).WillRepeatedly(Return(true));
EXPECT_TRUE(DefaultSortingOrderIs(service10, service2));
// Online preferred over just connected.
EXPECT_CALL(*service2, 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));
}
TEST_F(ServiceTest, ComparePreferEthernetOverWifi) {
// Create mock ethernet service.
scoped_refptr<MockService> ethernet_service(
new NiceMock<MockService>(manager()));
EXPECT_CALL(*ethernet_service.get(), technology())
.WillRepeatedly(Return(Technology::kEthernet));
// Create mock wifi service.
scoped_refptr<MockService> wifi_service(new NiceMock<MockService>(manager()));
EXPECT_CALL(*wifi_service.get(), technology())
.WillRepeatedly(Return((Technology::kWifi)));
// Confirm that ethernet service is sorted above wifi service.
technology_order_for_sorting_ = {Technology::kEthernet, Technology::kWifi};
EXPECT_TRUE(DefaultSortingOrderIs(ethernet_service, wifi_service));
// Even making the wifi service managed doesn't change the network sorting
// order.
wifi_service->managed_credentials_ = true;
EXPECT_TRUE(DefaultSortingOrderIs(ethernet_service, wifi_service));
}
TEST_F(ServiceTest, SanitizeStorageIdentifier) {
EXPECT_EQ("", Service::SanitizeStorageIdentifier(""));
for (int c = 0; c < 256; ++c) {
std::string identifier(1, c);
std::string sanitized_identifier = std::isalnum(c) ? identifier : "_";
EXPECT_EQ(sanitized_identifier,
Service::SanitizeStorageIdentifier(identifier));
}
EXPECT_EQ("service_1_2_3_2_Dummy_Net_",
Service::SanitizeStorageIdentifier("service_1-2:3.2_Dummy^Net!"));
}
TEST_F(ServiceTest, DisconnectSetsDisconnectState) {
EXPECT_EQ(service_->state(), Service::kStateIdle);
// Inactive Service will immediately fail a Disconnect call.
Error error;
service_->Disconnect(&error, __func__);
EXPECT_EQ(error.type(), Error::kNotConnected);
EXPECT_EQ(service_->state(), Service::kStateIdle);
// Non-disconnectable Service will also immediately fail a Disconnect call.
service_->SetDisconnectable(false);
error.Reset();
service_->Disconnect(&error, __func__);
EXPECT_EQ(error.type(), Error::kNotConnected);
EXPECT_EQ(service_->state(), Service::kStateIdle);
service_->SetDisconnectable(true);
// Otherwise the state will be driven to kStateDisconnecting.
service_->SetState(Service::kStateAssociating);
error.Reset();
service_->Disconnect(&error, __func__);
EXPECT_EQ(error.type(), Error::kSuccess);
EXPECT_EQ(service_->state(), Service::kStateDisconnecting);
}
TEST_F(ServiceTest, DelayedDisconnect) {
// Any state that causes IsActive to return true will do.
service_->SetState(Service::kStateAssociating);
// Begin disconnect but do not finish.
Error error;
service_->Disconnect(&error, __func__);
EXPECT_EQ(error.type(), Error::kSuccess);
EXPECT_EQ(service_->state(), Service::kStateDisconnecting);
// Trigger connection.
ASSERT_TRUE(service_->connectable());
service_->Connect(&error, __func__);
EXPECT_EQ(error.type(), Error::kSuccess);
EXPECT_EQ(service_->state(), Service::kStateDisconnecting);
EXPECT_TRUE(HasPendingConnect());
// Finish the disconnection by driving state to kStateIdle.
service_->SetState(Service::kStateIdle);
EXPECT_TRUE(HasPendingConnect());
// Invoke the pending disconnect task.
GetPendingConnectTask().callback().Run();
EXPECT_FALSE(HasPendingConnect());
}
TEST_F(ServiceTest, DelayedDisconnectWithAdditionalConnect) {
// Any state that causes IsActive to return true will do.
service_->SetState(Service::kStateAssociating);
// Begin disconnect but do not finish.
Error error;
service_->Disconnect(&error, __func__);
EXPECT_EQ(error.type(), Error::kSuccess);
EXPECT_EQ(service_->state(), Service::kStateDisconnecting);
// Trigger connection.
ASSERT_TRUE(service_->connectable());
service_->Connect(&error, __func__);
EXPECT_EQ(error.type(), Error::kSuccess);
EXPECT_EQ(service_->state(), Service::kStateDisconnecting);
EXPECT_TRUE(HasPendingConnect());
// Finish the disconnection by driving state to kStateIdle.
service_->SetState(Service::kStateIdle);
EXPECT_TRUE(HasPendingConnect());
// Trigger connection prior to the pending connect task being run;
// ensure that the pending connect task will be cancelled.
ASSERT_TRUE(service_->connectable());
service_->Connect(&error, __func__);
EXPECT_EQ(error.type(), Error::kSuccess);
EXPECT_FALSE(HasPendingConnect());
}
TEST_F(ServiceTest, TrafficCounters) {
patchpanel::TrafficCounter counter0, counter1;
counter0.set_source(patchpanel::TrafficCounter::CHROME);
counter0.set_rx_bytes(12);
counter0.set_tx_bytes(34);
counter0.set_rx_packets(56);
counter0.set_tx_packets(78);
counter1.set_source(patchpanel::TrafficCounter::USER);
counter1.set_rx_bytes(90);
counter1.set_tx_bytes(87);
counter1.set_rx_packets(65);
counter1.set_tx_packets(43);
service_->InitializeTrafficCounterSnapshot({counter0, counter1});
EXPECT_EQ(service_->traffic_counter_snapshot_.size(), 2);
vector<uint64_t> chrome_counters{12, 34, 56, 78};
vector<uint64_t> user_counters{90, 87, 65, 43};
for (size_t i = 0; i < Service::kTrafficCounterArraySize; i++) {
EXPECT_EQ(
service_
->traffic_counter_snapshot_[patchpanel::TrafficCounter::CHROME][i],
chrome_counters[i]);
EXPECT_EQ(
service_
->traffic_counter_snapshot_[patchpanel::TrafficCounter::USER][i],
user_counters[i]);
}
EXPECT_EQ(service_->current_traffic_counters_.size(), 0);
counter0.set_rx_bytes(20);
counter0.set_tx_bytes(40);
counter0.set_rx_packets(60);
counter0.set_tx_packets(80);
counter1.set_rx_bytes(100);
counter1.set_tx_bytes(90);
counter1.set_rx_packets(80);
counter1.set_tx_packets(70);
service_->RefreshTrafficCounters({counter0, counter1});
EXPECT_EQ(service_->traffic_counter_snapshot_.size(), 2);
chrome_counters = {20, 40, 60, 80};
user_counters = {100, 90, 80, 70};
for (size_t i = 0; i < Service::kTrafficCounterArraySize; i++) {
EXPECT_EQ(
service_
->traffic_counter_snapshot_[patchpanel::TrafficCounter::CHROME][i],
chrome_counters[i]);
EXPECT_EQ(
service_
->traffic_counter_snapshot_[patchpanel::TrafficCounter::USER][i],
user_counters[i]);
}
EXPECT_EQ(service_->current_traffic_counters_.size(), 2);
vector<uint64_t> chrome_counters_diff{8, 6, 4, 2};
vector<uint64_t> user_counters_diff{10, 3, 15, 27};
for (size_t i = 0; i < Service::kTrafficCounterArraySize; i++) {
EXPECT_EQ(
service_
->current_traffic_counters_[patchpanel::TrafficCounter::CHROME][i],
chrome_counters_diff[i]);
EXPECT_EQ(
service_
->current_traffic_counters_[patchpanel::TrafficCounter::USER][i],
user_counters_diff[i]);
}
}
} // namespace shill