blob: 940988dc58e922776a72654dd60af6de6d7570e8 [file] [log] [blame]
// Copyright (c) 2012 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/wifi_service.h"
#include <limits>
#include <map>
#include <set>
#include <string>
#include <vector>
#include <base/strings/stringprintf.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include <chromeos/dbus/service_constants.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "shill/event_dispatcher.h"
#include "shill/manager.h"
#include "shill/metrics.h"
#include "shill/mock_adaptors.h"
#include "shill/mock_certificate_file.h"
#include "shill/mock_control.h"
#include "shill/mock_eap_credentials.h"
#include "shill/mock_log.h"
#include "shill/mock_manager.h"
#include "shill/mock_profile.h"
#include "shill/mock_service.h"
#include "shill/mock_store.h"
#include "shill/mock_wifi.h"
#include "shill/mock_wifi_provider.h"
#include "shill/property_store_unittest.h"
#include "shill/refptr_types.h"
#include "shill/service_property_change_test.h"
#include "shill/technology.h"
#include "shill/tethering.h"
#include "shill/wifi_endpoint.h"
#include "shill/wpa_supplicant.h"
using base::FilePath;
using std::map;
using std::set;
using std::string;
using std::vector;
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::DoAll;
using ::testing::EndsWith;
using ::testing::HasSubstr;
using ::testing::Mock;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::ReturnRef;
using ::testing::SetArgumentPointee;
using ::testing::StrEq;
using ::testing::StrNe;
using ::testing::StrictMock;
namespace shill {
class WiFiServiceTest : public PropertyStoreTest {
public:
WiFiServiceTest()
: mock_manager_(control_interface(), dispatcher(), metrics(), glib()),
wifi_(
new NiceMock<MockWiFi>(control_interface(),
dispatcher(),
metrics(),
manager(),
"wifi",
fake_mac,
0)),
simple_ssid_(1, 'a'),
simple_ssid_string_("a") {}
virtual ~WiFiServiceTest() {}
protected:
static const char fake_mac[];
MockEapCredentials *SetMockEap(
const WiFiServiceRefPtr &service) {
MockEapCredentials *eap = new MockEapCredentials();
service->eap_.reset(eap); // Passes ownership.
return eap;
}
bool CheckConnectable(const string &security, const char *passphrase,
bool is_1x_connectable) {
Error error;
WiFiServiceRefPtr service = MakeSimpleService(security);
if (passphrase)
service->SetPassphrase(passphrase, &error);
MockEapCredentials *eap = SetMockEap(service);
EXPECT_CALL(*eap, IsConnectable())
.WillRepeatedly(Return(is_1x_connectable));
const string kKeyManagement8021x(WPASupplicant::kKeyManagementIeee8021X);
if (security == kSecurityWep && is_1x_connectable) {
EXPECT_CALL(*eap, key_management())
.WillRepeatedly(ReturnRef(kKeyManagement8021x));
}
service->OnEapCredentialsChanged();
return service->connectable();
}
WiFiEndpoint *MakeEndpoint(const string &ssid, const string &bssid,
uint16 frequency, int16 signal_dbm,
bool has_wpa_property, bool has_rsn_property) {
return WiFiEndpoint::MakeEndpoint(
NULL, wifi(), ssid, bssid, WPASupplicant::kNetworkModeInfrastructure,
frequency, signal_dbm, has_wpa_property, has_rsn_property);
}
WiFiEndpoint *MakeOpenEndpoint(const string &ssid, const string &bssid,
uint16 frequency, int16 signal_dbm) {
return WiFiEndpoint::MakeOpenEndpoint(
NULL, wifi(), ssid, bssid, WPASupplicant::kNetworkModeInfrastructure,
frequency, signal_dbm);
}
WiFiServiceRefPtr MakeSimpleService(const string &security) {
return new WiFiService(control_interface(),
dispatcher(),
metrics(),
manager(),
&provider_,
simple_ssid_,
kModeManaged,
security,
false);
}
WiFiServiceRefPtr MakeGenericService() {
return MakeSimpleService(kSecurityWep);
}
void SetWiFi(WiFiServiceRefPtr service, WiFiRefPtr wifi) {
service->SetWiFi(wifi); // Has side-effects.
}
void SetWiFiForService(WiFiServiceRefPtr service, WiFiRefPtr wifi) {
service->wifi_ = wifi;
}
WiFiServiceRefPtr MakeServiceWithWiFi(const string &security) {
WiFiServiceRefPtr service = MakeSimpleService(security);
SetWiFiForService(service, wifi_);
return service;
}
WiFiServiceRefPtr MakeServiceWithMockManager() {
return new WiFiService(control_interface(),
dispatcher(),
metrics(),
&mock_manager_,
&provider_,
simple_ssid_,
kModeManaged,
kSecurityWep,
false);
}
ServiceMockAdaptor *GetAdaptor(WiFiService *service) {
return dynamic_cast<ServiceMockAdaptor *>(service->adaptor());
}
Error::Type TestConfigurePassphrase(const string &security,
const char *passphrase) {
WiFiServiceRefPtr service = MakeSimpleService(security);
KeyValueStore args;
if (passphrase) {
args.SetString(kPassphraseProperty, passphrase);
}
Error error;
service->Configure(args, &error);
return error.type();
}
scoped_refptr<MockWiFi> wifi() { return wifi_; }
MockManager *mock_manager() { return &mock_manager_; }
MockWiFiProvider *provider() { return &provider_; }
string GetAnyDeviceAddress() { return WiFiService::kAnyDeviceAddress; }
const vector<uint8_t> &simple_ssid() { return simple_ssid_; }
const string &simple_ssid_string() { return simple_ssid_string_; }
private:
MockManager mock_manager_;
scoped_refptr<MockWiFi> wifi_;
MockWiFiProvider provider_;
const vector<uint8_t> simple_ssid_;
const string simple_ssid_string_;
};
// static
const char WiFiServiceTest::fake_mac[] = "AaBBcCDDeeFF";
MATCHER_P3(ContainsWiFiProperties, ssid, mode, security, "") {
string hex_ssid = base::HexEncode(ssid.data(), ssid.size());
return
arg.ContainsString(WiFiService::kStorageType) &&
arg.GetString(WiFiService::kStorageType) == kTypeWifi &&
arg.ContainsString(WiFiService::kStorageSSID) &&
arg.GetString(WiFiService::kStorageSSID) == hex_ssid &&
arg.ContainsString(WiFiService::kStorageMode) &&
arg.GetString(WiFiService::kStorageMode) == mode &&
arg.ContainsString(WiFiService::kStorageSecurityClass) &&
arg.GetString(WiFiService::kStorageSecurityClass) == security;
}
class WiFiServiceSecurityTest : public WiFiServiceTest {
public:
bool TestStorageSecurityIs(WiFiServiceRefPtr wifi_service,
const string &security) {
string id = wifi_service->GetStorageIdentifier();
size_t mac_pos = id.find(StringToLowerASCII(GetAnyDeviceAddress()));
EXPECT_NE(mac_pos, string::npos);
size_t mode_pos = id.find(string(kModeManaged), mac_pos);
EXPECT_NE(mode_pos, string::npos);
return id.find(string(security), mode_pos) != string::npos;
}
// Test that a service that is created with security |from_security|
// gets by default a storage identifier with |to_security| as its
// security component, and that when saved, it sets the Security
// property in to |to_security| as well.
bool TestStorageMapping(const string &from_security,
const string &to_security) {
WiFiServiceRefPtr wifi_service = MakeSimpleService(from_security);
NiceMock<MockStore> mock_store;
EXPECT_CALL(mock_store, SetString(_, _, _)).WillRepeatedly(Return(true));
EXPECT_CALL(mock_store,
SetString(_, WiFiService::kStorageSecurity, from_security))
.Times(1);
EXPECT_CALL(mock_store,
SetString(_, WiFiService::kStorageSecurityClass, to_security))
.Times(1);
wifi_service->Save(&mock_store);
return TestStorageSecurityIs(wifi_service, to_security);
}
// Test whether a service of type |service_security| can load from a
// storage interface containing an entry for |storage_security|.
// Make sure the result meets |expectation|. If |expectation| is
// true, also make sure the service storage identifier changes to
// match |storage_security|.
bool TestLoadMapping(const string &service_security,
const string &storage_security,
bool expectation) {
WiFiServiceRefPtr wifi_service = MakeSimpleService(service_security);
NiceMock<MockStore> mock_store;
EXPECT_CALL(mock_store, GetGroupsWithProperties(_))
.WillRepeatedly(Return(set<string>()));
const string kStorageId = "storage_id";
EXPECT_CALL(mock_store, ContainsGroup(kStorageId))
.WillRepeatedly(Return(true));
set<string> groups;
groups.insert(kStorageId);
EXPECT_CALL(mock_store, GetGroupsWithProperties(
ContainsWiFiProperties(wifi_service->ssid(),
kModeManaged,
storage_security)))
.WillRepeatedly(Return(groups));
bool is_loadable = wifi_service->IsLoadableFrom(mock_store);
EXPECT_EQ(expectation, is_loadable);
bool is_loaded = wifi_service->Load(&mock_store);
EXPECT_EQ(expectation, is_loaded);
const string expected_identifier(expectation ? kStorageId : "");
EXPECT_EQ(expected_identifier,
wifi_service->GetLoadableStorageIdentifier(mock_store));
if (expectation != is_loadable || expectation != is_loaded) {
return false;
} else if (!expectation) {
return true;
} else {
return wifi_service->GetStorageIdentifier() == kStorageId;
}
}
};
class WiFiServiceUpdateFromEndpointsTest : public WiFiServiceTest {
public:
WiFiServiceUpdateFromEndpointsTest()
: kOkEndpointStrength(WiFiService::SignalToStrength(kOkEndpointSignal)),
kBadEndpointStrength(WiFiService::SignalToStrength(kBadEndpointSignal)),
kGoodEndpointStrength(
WiFiService::SignalToStrength(kGoodEndpointSignal)),
service(MakeGenericService()),
adaptor(*GetAdaptor(service)) {
ok_endpoint = MakeOpenEndpoint(
simple_ssid_string(), kOkEndpointBssId, kOkEndpointFrequency,
kOkEndpointSignal);
good_endpoint = MakeOpenEndpoint(
simple_ssid_string(), kGoodEndpointBssId, kGoodEndpointFrequency,
kGoodEndpointSignal);
bad_endpoint = MakeOpenEndpoint(
simple_ssid_string(), kBadEndpointBssId, kBadEndpointFrequency,
kBadEndpointSignal);
}
protected:
static const uint16 kOkEndpointFrequency = 2422;
static const uint16 kBadEndpointFrequency = 2417;
static const uint16 kGoodEndpointFrequency = 2412;
static const int16 kOkEndpointSignal = -50;
static const int16 kBadEndpointSignal = -75;
static const int16 kGoodEndpointSignal = -25;
static const char *kOkEndpointBssId;
static const char *kGoodEndpointBssId;
static const char *kBadEndpointBssId;
// Can't be both static and const (because initialization requires a
// function call). So choose to be just const.
const uint8 kOkEndpointStrength;
const uint8 kBadEndpointStrength;
const uint8 kGoodEndpointStrength;
WiFiEndpointRefPtr ok_endpoint;
WiFiEndpointRefPtr bad_endpoint;
WiFiEndpointRefPtr good_endpoint;
WiFiServiceRefPtr service;
ServiceMockAdaptor &adaptor;
};
const char *WiFiServiceUpdateFromEndpointsTest::kOkEndpointBssId =
"00:00:00:00:00:01";
const char *WiFiServiceUpdateFromEndpointsTest::kGoodEndpointBssId =
"00:00:00:00:00:02";
const char *WiFiServiceUpdateFromEndpointsTest::kBadEndpointBssId =
"00:00:00:00:00:03";
class WiFiServiceFixupStorageTest : public WiFiServiceTest {
protected:
void AddGroup(string group_name) {
groups_.insert(group_name);
}
void AddServiceEntry(bool has_type, bool has_mode, bool has_security,
bool has_security_class) {
int index = groups_.size();
string id = base::StringPrintf("%s_%d_%d_%s_%s", kTypeWifi,
index, index, kModeManaged,
kSecurityWpa);
AddGroup(id);
EXPECT_CALL(store_, GetString(id, WiFiService::kStorageType, _))
.WillOnce(Return(has_type));
if (!has_type) {
EXPECT_CALL(store_, SetString(id, WiFiService::kStorageType,
kTypeWifi));
}
EXPECT_CALL(store_, GetString(id, WiFiService::kStorageMode, _))
.WillOnce(Return(has_mode));
if (!has_mode) {
EXPECT_CALL(store_, SetString(id, WiFiService::kStorageMode,
kModeManaged));
}
EXPECT_CALL(store_, GetString(id, WiFiService::kStorageSecurity, _))
.WillOnce(Return(has_security));
if (!has_security) {
EXPECT_CALL(store_, SetString(id, WiFiService::kStorageSecurity,
kSecurityWpa));
}
EXPECT_CALL(store_, GetString(id, WiFiService::kStorageSecurityClass, _))
.WillOnce(Return(has_security_class));
if (!has_security_class) {
EXPECT_CALL(store_, SetString(id, WiFiService::kStorageSecurityClass,
kSecurityPsk));
}
}
bool FixupServiceEntries() {
EXPECT_CALL(store_, GetGroups()).WillOnce(Return(groups_));
return WiFiService::FixupServiceEntries(&store_);
}
private:
StrictMock<MockStore> store_;
set<string> groups_;
};
TEST_F(WiFiServiceTest, Constructor) {
string histogram = metrics()->GetFullMetricName(
Metrics::kMetricTimeToJoinMillisecondsSuffix, Technology::kWifi);
EXPECT_CALL(*metrics(), AddServiceStateTransitionTimer(_, _, _, _))
.Times(AnyNumber());
EXPECT_CALL(*metrics(), AddServiceStateTransitionTimer(
_, histogram, Service::kStateAssociating, Service::kStateConfiguring));
MakeSimpleService(kSecurityNone);
}
TEST_F(WiFiServiceTest, StorageId) {
WiFiServiceRefPtr wifi_service = MakeSimpleService(kSecurityNone);
string id = wifi_service->GetStorageIdentifier();
for (uint i = 0; i < id.length(); ++i) {
EXPECT_TRUE(id[i] == '_' ||
isxdigit(id[i]) ||
(isalpha(id[i]) && islower(id[i])));
}
size_t mac_pos = id.find(StringToLowerASCII(GetAnyDeviceAddress()));
EXPECT_NE(mac_pos, string::npos);
EXPECT_NE(id.find(string(kModeManaged), mac_pos), string::npos);
}
// Make sure the passphrase is registered as a write only property
// by reading and comparing all string properties returned on the store.
TEST_F(WiFiServiceTest, PassphraseWriteOnly) {
WiFiServiceRefPtr wifi_service = MakeSimpleService(kSecurityWpa);
ReadablePropertyConstIterator<string> it =
(wifi_service->store()).GetStringPropertiesIter();
for ( ; !it.AtEnd(); it.Advance())
EXPECT_NE(it.Key(), kPassphraseProperty);
}
// Make sure setting the passphrase via D-Bus Service.SetProperty validates
// the passphrase.
TEST_F(WiFiServiceTest, PassphraseSetPropertyValidation) {
// We only spot check two password cases here to make sure the
// SetProperty code path does validation. We're not going to exhaustively
// test for all types of passwords.
WiFiServiceRefPtr wifi_service = MakeSimpleService(kSecurityWep);
Error error;
EXPECT_TRUE(wifi_service->mutable_store()->SetStringProperty(
kPassphraseProperty, "0:abcde", &error));
EXPECT_FALSE(wifi_service->mutable_store()->SetStringProperty(
kPassphraseProperty, "invalid", &error));
EXPECT_EQ(Error::kInvalidPassphrase, error.type());
}
TEST_F(WiFiServiceTest, PassphraseSetPropertyOpenNetwork) {
WiFiServiceRefPtr wifi_service = MakeSimpleService(kSecurityNone);
Error error;
EXPECT_FALSE(wifi_service->mutable_store()->SetStringProperty(
kPassphraseProperty, "invalid", &error));
EXPECT_EQ(Error::kNotSupported, error.type());
}
TEST_F(WiFiServiceTest, NonUTF8SSID) {
vector<uint8_t> ssid;
ssid.push_back(0xff); // not a valid UTF-8 byte-sequence
WiFiServiceRefPtr wifi_service = new WiFiService(control_interface(),
dispatcher(),
metrics(),
manager(),
provider(),
ssid,
kModeManaged,
kSecurityNone,
false);
map<string, ::DBus::Variant> properties;
// if service doesn't propertly sanitize SSID, this will generate SIGABRT.
DBusAdaptor::GetProperties(wifi_service->store(), &properties, NULL);
}
MATCHER(PSKSecurityArgs, "") {
return ContainsKey(arg, WPASupplicant::kPropertySecurityProtocol) &&
arg.find(WPASupplicant::kPropertySecurityProtocol)->second.
reader().get_string() == string("WPA RSN") &&
ContainsKey(arg, WPASupplicant::kPropertyPreSharedKey);
}
MATCHER_P(FrequencyArg, has_arg, "") {
return has_arg ==
ContainsKey(arg, WPASupplicant::kNetworkPropertyFrequency);
}
TEST_F(WiFiServiceTest, ConnectReportBSSes) {
WiFiEndpointRefPtr endpoint1 =
MakeOpenEndpoint("a", "00:00:00:00:00:01", 0, 0);
WiFiEndpointRefPtr endpoint2 =
MakeOpenEndpoint("a", "00:00:00:00:00:02", 0, 0);
WiFiServiceRefPtr wifi_service = MakeServiceWithWiFi(kSecurityNone);
wifi_service->AddEndpoint(endpoint1);
wifi_service->AddEndpoint(endpoint2);
EXPECT_CALL(*metrics(), NotifyWifiAvailableBSSes(2));
EXPECT_CALL(*wifi(), ConnectTo(wifi_service.get()));
wifi_service->Connect(NULL, "in test");
}
TEST_F(WiFiServiceTest, ConnectTaskWPA) {
WiFiServiceRefPtr wifi_service = MakeServiceWithWiFi(kSecurityWpa);
EXPECT_CALL(*wifi(), ConnectTo(wifi_service.get()));
Error error;
wifi_service->SetPassphrase("0:mumblemumblem", &error);
wifi_service->Connect(NULL, "in test");
EXPECT_THAT(wifi_service->GetSupplicantConfigurationParameters(),
PSKSecurityArgs());
}
TEST_F(WiFiServiceTest, ConnectTaskRSN) {
WiFiServiceRefPtr wifi_service = MakeServiceWithWiFi(kSecurityRsn);
EXPECT_CALL(*wifi(), ConnectTo(wifi_service.get()));
Error error;
wifi_service->SetPassphrase("0:mumblemumblem", &error);
wifi_service->Connect(NULL, "in test");
EXPECT_THAT(wifi_service->GetSupplicantConfigurationParameters(),
PSKSecurityArgs());
}
TEST_F(WiFiServiceTest, ConnectConditions) {
Error error;
WiFiServiceRefPtr wifi_service = MakeServiceWithWiFi(kSecurityNone);
scoped_refptr<MockProfile> mock_profile(
new NiceMock<MockProfile>(control_interface(), metrics(), manager()));
wifi_service->set_profile(mock_profile);
// With nothing else going on, the service should attempt to connect.
EXPECT_CALL(*wifi(), ConnectTo(wifi_service.get()));
wifi_service->Connect(&error, "in test");
Mock::VerifyAndClearExpectations(wifi());
// But if we're already "connecting" or "connected" then we shouldn't attempt
// again.
EXPECT_CALL(*wifi(), ConnectTo(wifi_service.get())).Times(0);
wifi_service->SetState(Service::kStateAssociating);
wifi_service->Connect(&error, "in test");
wifi_service->SetState(Service::kStateConfiguring);
wifi_service->Connect(&error, "in test");
wifi_service->SetState(Service::kStateConnected);
wifi_service->Connect(&error, "in test");
wifi_service->SetState(Service::kStatePortal);
wifi_service->Connect(&error, "in test");
wifi_service->SetState(Service::kStateOnline);
wifi_service->Connect(&error, "in test");
Mock::VerifyAndClearExpectations(wifi());
}
TEST_F(WiFiServiceTest, ConnectTaskPSK) {
WiFiServiceRefPtr wifi_service = MakeServiceWithWiFi(kSecurityPsk);
EXPECT_CALL(*wifi(), ConnectTo(wifi_service.get()));
Error error;
wifi_service->SetPassphrase("0:mumblemumblem", &error);
wifi_service->Connect(NULL, "in test");
EXPECT_THAT(wifi_service->GetSupplicantConfigurationParameters(),
PSKSecurityArgs());
}
TEST_F(WiFiServiceTest, ConnectTask8021x) {
WiFiServiceRefPtr service = MakeServiceWithWiFi(kSecurity8021x);
service->mutable_eap()->set_identity("identity");
service->mutable_eap()->set_password("mumble");
service->OnEapCredentialsChanged();
EXPECT_CALL(*wifi(), ConnectTo(service.get()));
service->Connect(NULL, "in test");
DBusPropertiesMap params = service->GetSupplicantConfigurationParameters();
EXPECT_TRUE(ContainsKey(params, WPASupplicant::kNetworkPropertyEapIdentity));
EXPECT_TRUE(ContainsKey(params, WPASupplicant::kNetworkPropertyCaPath));
}
TEST_F(WiFiServiceTest, ConnectTask8021xWithMockEap) {
WiFiServiceRefPtr service = MakeServiceWithWiFi(kSecurity8021x);
MockEapCredentials *eap = SetMockEap(service);
EXPECT_CALL(*eap, IsConnectable()).WillOnce(Return(true));
EXPECT_CALL(*wifi(), ConnectTo(service.get()));
service->OnEapCredentialsChanged();
service->Connect(NULL, "in test");
EXPECT_CALL(*eap, PopulateSupplicantProperties(_, _));
// The mocked function does not actually set EAP parameters so we cannot
// expect them to be set.
service->GetSupplicantConfigurationParameters();
}
TEST_F(WiFiServiceTest, ConnectTaskAdHocFrequency) {
vector<uint8_t> ssid(1, 'a');
WiFiEndpointRefPtr endpoint_nofreq =
MakeOpenEndpoint("a", "00:00:00:00:00:01", 0, 0);
WiFiEndpointRefPtr endpoint_freq =
MakeOpenEndpoint("a", "00:00:00:00:00:02", 2412, 0);
WiFiServiceRefPtr wifi_service = MakeServiceWithWiFi(kSecurityNone);
wifi_service->AddEndpoint(endpoint_freq);
EXPECT_CALL(*wifi(), ConnectTo(wifi_service.get()));
wifi_service->Connect(NULL, "in test");
EXPECT_THAT(wifi_service->GetSupplicantConfigurationParameters(),
FrequencyArg(false));
wifi_service = new WiFiService(control_interface(),
dispatcher(),
metrics(),
manager(),
provider(),
ssid,
kModeAdhoc,
kSecurityNone,
false);
EXPECT_CALL(*wifi(), ConnectTo(wifi_service.get()));
SetWiFiForService(wifi_service, wifi());
wifi_service->Connect(NULL, "in test");
EXPECT_THAT(wifi_service->GetSupplicantConfigurationParameters(),
FrequencyArg(false));
wifi_service = new WiFiService(control_interface(),
dispatcher(),
metrics(),
manager(),
provider(),
ssid,
kModeAdhoc,
kSecurityNone,
false);
wifi_service->AddEndpoint(endpoint_nofreq);
SetWiFiForService(wifi_service, wifi());
EXPECT_CALL(*wifi(), ConnectTo(wifi_service.get()));
wifi_service->Connect(NULL, "in test");
EXPECT_THAT(wifi_service->GetSupplicantConfigurationParameters(),
FrequencyArg(false));
wifi_service = new WiFiService(control_interface(),
dispatcher(),
metrics(),
manager(),
provider(),
ssid,
kModeAdhoc,
kSecurityNone,
false);
wifi_service->AddEndpoint(endpoint_freq);
SetWiFiForService(wifi_service, wifi());
EXPECT_CALL(*wifi(), ConnectTo(wifi_service.get()));
wifi_service->Connect(NULL, "in test");
EXPECT_THAT(wifi_service->GetSupplicantConfigurationParameters(),
FrequencyArg(true));
}
TEST_F(WiFiServiceTest, ConnectTaskWPA80211w) {
WiFiServiceRefPtr wifi_service = MakeServiceWithWiFi(kSecurityPsk);
WiFiEndpointRefPtr endpoint =
MakeOpenEndpoint("a", "00:00:00:00:00:01", 0, 0);
endpoint->ieee80211w_required_ = true;
wifi_service->AddEndpoint(endpoint);
Error error;
wifi_service->SetPassphrase("0:mumblemumblem", &error);
EXPECT_CALL(*wifi(), ConnectTo(wifi_service.get()));
wifi_service->Connect(NULL, "in test");
DBusPropertiesMap params =
wifi_service->GetSupplicantConfigurationParameters();
EXPECT_TRUE(ContainsKey(params, WPASupplicant::kPropertySecurityProtocol));
EXPECT_TRUE(ContainsKey(params, WPASupplicant::kPropertyPreSharedKey));
EXPECT_TRUE(ContainsKey(params, WPASupplicant::kNetworkPropertyIeee80211w));
}
MATCHER_P(WEPSecurityArgsKeyIndex, index, "") {
uint32 index_u32 = index;
return ContainsKey(arg, WPASupplicant::kPropertyAuthAlg) &&
ContainsKey(arg,
WPASupplicant::kPropertyWEPKey + base::IntToString(index)) &&
ContainsKey(arg, WPASupplicant::kPropertyWEPTxKeyIndex) &&
(arg.find(WPASupplicant::kPropertyWEPTxKeyIndex)->second.
reader().get_uint32() == index_u32);
}
TEST_F(WiFiServiceTest, ConnectTaskWEP) {
WiFiServiceRefPtr wifi_service = MakeServiceWithWiFi(kSecurityWep);
Error error;
wifi_service->SetPassphrase("0:abcdefghijklm", &error);
EXPECT_CALL(*wifi(), ConnectTo(wifi_service.get()));
wifi_service->Connect(NULL, "in test");
EXPECT_THAT(wifi_service->GetSupplicantConfigurationParameters(),
WEPSecurityArgsKeyIndex(0));
wifi_service->SetPassphrase("abcdefghijklm", &error);
EXPECT_CALL(*wifi(), ConnectTo(wifi_service.get()));
wifi_service->Connect(NULL, "in test");
EXPECT_THAT(wifi_service->GetSupplicantConfigurationParameters(),
WEPSecurityArgsKeyIndex(0));
wifi_service->SetPassphrase("1:abcdefghijklm", &error);
EXPECT_CALL(*wifi(), ConnectTo(wifi_service.get()));
wifi_service->Connect(NULL, "in test");
EXPECT_THAT(wifi_service->GetSupplicantConfigurationParameters(),
WEPSecurityArgsKeyIndex(1));
wifi_service->SetPassphrase("2:abcdefghijklm", &error);
EXPECT_CALL(*wifi(), ConnectTo(wifi_service.get()));
wifi_service->Connect(NULL, "in test");
EXPECT_THAT(wifi_service->GetSupplicantConfigurationParameters(),
WEPSecurityArgsKeyIndex(2));
wifi_service->SetPassphrase("3:abcdefghijklm", &error);
EXPECT_CALL(*wifi(), ConnectTo(wifi_service.get()));
wifi_service->Connect(NULL, "in test");
EXPECT_THAT(wifi_service->GetSupplicantConfigurationParameters(),
WEPSecurityArgsKeyIndex(3));
}
// Dynamic WEP + 802.1x.
TEST_F(WiFiServiceTest, ConnectTaskDynamicWEP) {
WiFiServiceRefPtr wifi_service = MakeServiceWithWiFi(kSecurityWep);
wifi_service->mutable_eap()->SetKeyManagement("IEEE8021X", NULL);
wifi_service->mutable_eap()->set_identity("something");
wifi_service->mutable_eap()->set_password("mumble");
wifi_service->OnEapCredentialsChanged();
EXPECT_CALL(*wifi(), ConnectTo(wifi_service.get()));
wifi_service->Connect(NULL, "in test");
DBusPropertiesMap params =
wifi_service->GetSupplicantConfigurationParameters();
EXPECT_TRUE(ContainsKey(params, WPASupplicant::kNetworkPropertyEapIdentity));
EXPECT_TRUE(ContainsKey(params, WPASupplicant::kNetworkPropertyCaPath));
EXPECT_FALSE(ContainsKey(params, WPASupplicant::kPropertySecurityProtocol));
}
TEST_F(WiFiServiceTest, SetPassphraseRemovesCachedCredentials) {
WiFiServiceRefPtr wifi_service = MakeServiceWithWiFi(kSecurityRsn);
const string kPassphrase = "abcdefgh";
{
Error error;
// A changed passphrase should trigger cache removal.
EXPECT_CALL(*wifi(), ClearCachedCredentials(wifi_service.get()));
wifi_service->SetPassphrase(kPassphrase, &error);
Mock::VerifyAndClearExpectations(wifi());
EXPECT_TRUE(error.IsSuccess());
}
{
Error error;
// An unchanged passphrase should not trigger cache removal.
EXPECT_CALL(*wifi(), ClearCachedCredentials(_)).Times(0);
wifi_service->SetPassphrase(kPassphrase, &error);
Mock::VerifyAndClearExpectations(wifi());
EXPECT_TRUE(error.IsSuccess());
}
{
Error error;
// A modified passphrase should trigger cache removal.
EXPECT_CALL(*wifi(), ClearCachedCredentials(wifi_service.get()));
wifi_service->SetPassphrase(kPassphrase + "X", &error);
Mock::VerifyAndClearExpectations(wifi());
EXPECT_TRUE(error.IsSuccess());
}
{
Error error;
// A cleared passphrase should also trigger cache removal.
EXPECT_CALL(*wifi(), ClearCachedCredentials(wifi_service.get()));
wifi_service->ClearPassphrase(&error);
Mock::VerifyAndClearExpectations(wifi());
EXPECT_TRUE(error.IsSuccess());
}
{
Error error;
// An invalid passphrase should not trigger cache removal.
EXPECT_CALL(*wifi(), ClearCachedCredentials(_)).Times(0);
wifi_service->SetPassphrase("", &error);
Mock::VerifyAndClearExpectations(wifi());
EXPECT_FALSE(error.IsSuccess());
}
{
// Any change to EAP parameters (including a null one) will trigger cache
// removal. This is a lot less granular than the passphrase checks above.
EXPECT_CALL(*wifi(), ClearCachedCredentials(wifi_service.get()));
wifi_service->OnEapCredentialsChanged();
Mock::VerifyAndClearExpectations(wifi());
}
}
// This test is somewhat redundant, since:
//
// a) we test that generic property setters return false on a null
// change (e.g. in PropertyAccessorTest.SignedIntCorrectness)
// b) we test that custom EAP property setters return false on a null
// change in EapCredentialsTest.CustomSetterNoopChange
// c) we test that the various custom accessors pass through the
// return value of custom setters
// (e.g. PropertyAccessorTest.CustomAccessorCorrectness)
// d) we test that PropertyStore skips the change callback when a
// property setter return false (PropertyStoreTypedTest.SetProperty)
//
// Nonetheless, I think it's worth testing the WiFi+EAP case directly.
TEST_F(WiFiServiceTest, EapAuthPropertyChangeClearsCachedCredentials) {
WiFiServiceRefPtr wifi_service =
MakeServiceWithWiFi(kSecurity8021x);
PropertyStore &property_store(*wifi_service->mutable_store());
// Property with custom accessor.
const string kPassword = "abcdefgh";
{
Error error;
// A changed passphrase should trigger cache removal.
EXPECT_CALL(*wifi(), ClearCachedCredentials(wifi_service.get()));
EXPECT_TRUE(property_store.SetStringProperty(
kEapPasswordProperty, kPassword, &error));
Mock::VerifyAndClearExpectations(wifi());
EXPECT_TRUE(error.IsSuccess());
}
{
Error error;
// An unchanged passphrase should not trigger cache removal.
EXPECT_CALL(*wifi(), ClearCachedCredentials(_)).Times(0);
EXPECT_FALSE(property_store.SetStringProperty(
kEapPasswordProperty, kPassword, &error));
Mock::VerifyAndClearExpectations(wifi());
EXPECT_TRUE(error.IsSuccess());
}
{
Error error;
// A modified passphrase should trigger cache removal.
EXPECT_CALL(*wifi(), ClearCachedCredentials(wifi_service.get()));
EXPECT_TRUE(property_store.SetStringProperty(
kEapPasswordProperty, kPassword + "X", &error));
Mock::VerifyAndClearExpectations(wifi());
EXPECT_TRUE(error.IsSuccess());
}
// Property with generic accessor.
const string kCertId = "abcdefgh";
{
Error error;
// A changed cert id should trigger cache removal.
EXPECT_CALL(*wifi(), ClearCachedCredentials(wifi_service.get()));
EXPECT_TRUE(property_store.SetStringProperty(
kEapCertIdProperty, kCertId, &error));
Mock::VerifyAndClearExpectations(wifi());
EXPECT_TRUE(error.IsSuccess());
}
{
Error error;
// An unchanged cert id should not trigger cache removal.
EXPECT_CALL(*wifi(), ClearCachedCredentials(_)).Times(0);
EXPECT_FALSE(property_store.SetStringProperty(
kEapCertIdProperty, kCertId, &error));
Mock::VerifyAndClearExpectations(wifi());
EXPECT_TRUE(error.IsSuccess());
}
{
Error error;
// A modified cert id should trigger cache removal.
EXPECT_CALL(*wifi(), ClearCachedCredentials(wifi_service.get()));
EXPECT_TRUE(property_store.SetStringProperty(
kEapCertIdProperty, kCertId + "X", &error));
Mock::VerifyAndClearExpectations(wifi());
EXPECT_TRUE(error.IsSuccess());
}
}
TEST_F(WiFiServiceTest, LoadHidden) {
WiFiServiceRefPtr service = MakeSimpleService(kSecurityNone);
ASSERT_FALSE(service->hidden_ssid_);
NiceMock<MockStore> mock_store;
const string storage_id = service->GetStorageIdentifier();
set<string> groups;
groups.insert(storage_id);
EXPECT_CALL(mock_store, ContainsGroup(StrEq(storage_id)))
.WillRepeatedly(Return(true));
EXPECT_CALL(mock_store, GetGroupsWithProperties(
ContainsWiFiProperties(
simple_ssid(), kModeManaged, kSecurityNone)))
.WillRepeatedly(Return(groups));
EXPECT_CALL(mock_store, GetBool(_, _, _))
.WillRepeatedly(Return(false));
EXPECT_CALL(mock_store,
GetBool(StrEq(storage_id), WiFiService::kStorageHiddenSSID, _))
.WillRepeatedly(DoAll(SetArgumentPointee<2>(true), Return(true)));
EXPECT_TRUE(service->Load(&mock_store));
EXPECT_TRUE(service->hidden_ssid_);
}
TEST_F(WiFiServiceTest, LoadPassphraseForNonPassphraseService) {
WiFiServiceRefPtr service = MakeSimpleService(kSecurityNone);
NiceMock<MockStore> mock_store;
const string storage_id = service->GetStorageIdentifier();
set<string> groups;
groups.insert(storage_id);
EXPECT_CALL(mock_store, ContainsGroup(StrEq(storage_id)))
.WillRepeatedly(Return(true));
EXPECT_CALL(mock_store, GetGroupsWithProperties(
ContainsWiFiProperties(
simple_ssid(), kModeManaged, kSecurityNone)))
.WillRepeatedly(Return(groups));
EXPECT_CALL(mock_store,
GetCryptedString(StrEq(storage_id),
StrNe(WiFiService::kStoragePassphrase), _))
.WillRepeatedly(Return(false));
EXPECT_CALL(mock_store,
GetCryptedString(StrEq(storage_id),
WiFiService::kStoragePassphrase, _))
.WillOnce(DoAll(SetArgumentPointee<2>(string("password")), Return(true)))
.WillOnce(DoAll(SetArgumentPointee<2>(string()), Return(true)));
ScopedMockLog log;
EXPECT_CALL(log, Log(logging::LOG_ERROR, _,
HasSubstr("Passphrase could not be set: "
"org.chromium.flimflam.Error.NotSupported")))
.Times(1);
EXPECT_TRUE(service->Load(&mock_store));
Mock::VerifyAndClearExpectations(&log);
EXPECT_CALL(log, Log(logging::LOG_ERROR, _, _)).Times(0);
EXPECT_TRUE(service->Load(&mock_store));
}
TEST_F(WiFiServiceTest, LoadMultipleMatchingGroups) {
WiFiServiceRefPtr service = MakeServiceWithWiFi(kSecurityNone);
set<string> groups;
groups.insert("id0");
groups.insert("id1");
// Make sure we retain the first matched group in the same way that
// WiFiService::Load() will.
string first_group = *groups.begin();
NiceMock<MockStore> mock_store;
EXPECT_CALL(mock_store, GetGroupsWithProperties(
ContainsWiFiProperties(
simple_ssid(), kModeManaged, kSecurityNone)))
.WillRepeatedly(Return(groups));
EXPECT_CALL(mock_store, ContainsGroup(first_group))
.WillRepeatedly(Return(true));
EXPECT_CALL(mock_store, ContainsGroup(StrNe(first_group))).Times(0);
EXPECT_CALL(mock_store, GetBool(first_group, _, _))
.WillRepeatedly(Return(false));
EXPECT_CALL(mock_store, GetBool(StrNe(first_group), _, _)).Times(0);
ScopedMockLog log;
EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber());
EXPECT_CALL(log, Log(logging::LOG_WARNING, _,
EndsWith("choosing the first.")));
EXPECT_TRUE(service->Load(&mock_store));
}
TEST_F(WiFiServiceSecurityTest, WPAMapping) {
EXPECT_TRUE(TestStorageMapping(kSecurityRsn, kSecurityPsk));
EXPECT_TRUE(TestStorageMapping(kSecurityWpa, kSecurityPsk));
EXPECT_TRUE(TestStorageMapping(kSecurityPsk, kSecurityPsk));
EXPECT_TRUE(TestStorageMapping(kSecurityWep, kSecurityWep));
EXPECT_TRUE(TestStorageMapping(kSecurityNone, kSecurityNone));
EXPECT_TRUE(TestStorageMapping(kSecurity8021x, kSecurity8021x));
}
TEST_F(WiFiServiceSecurityTest, LoadMapping) {
EXPECT_TRUE(TestLoadMapping(kSecurityRsn, kSecurityPsk, true));
EXPECT_TRUE(TestLoadMapping(kSecurityRsn, kSecurityRsn, false));
EXPECT_TRUE(TestLoadMapping(kSecurityRsn, kSecurityWpa, false));
EXPECT_TRUE(TestLoadMapping(kSecurityWpa, kSecurityPsk, true));
EXPECT_TRUE(TestLoadMapping(kSecurityWpa, kSecurityWpa, false));
EXPECT_TRUE(TestLoadMapping(kSecurityWpa, kSecurityRsn, false));
EXPECT_TRUE(TestLoadMapping(kSecurityWep, kSecurityWep, true));
EXPECT_TRUE(TestLoadMapping(kSecurityWep, kSecurityPsk, false));
}
TEST_F(WiFiServiceTest, LoadAndUnloadPassphrase) {
WiFiServiceRefPtr service = MakeSimpleService(kSecurityPsk);
NiceMock<MockStore> mock_store;
const string storage_id = service->GetStorageIdentifier();
EXPECT_CALL(mock_store, ContainsGroup(StrEq(storage_id)))
.WillRepeatedly(Return(true));
set<string> groups;
groups.insert(storage_id);
EXPECT_CALL(mock_store, GetGroupsWithProperties(
ContainsWiFiProperties(
simple_ssid(), kModeManaged, kSecurityPsk)))
.WillRepeatedly(Return(groups));
EXPECT_CALL(mock_store, GetBool(_, _, _))
.WillRepeatedly(Return(false));
const string passphrase = "passphrase";
EXPECT_CALL(mock_store,
GetCryptedString(StrEq(storage_id),
WiFiService::kStoragePassphrase, _))
.WillRepeatedly(DoAll(SetArgumentPointee<2>(passphrase), Return(true)));
EXPECT_CALL(mock_store,
GetCryptedString(StrEq(storage_id),
StrNe(WiFiService::kStoragePassphrase), _))
.WillRepeatedly(Return(false));
EXPECT_TRUE(service->need_passphrase_);
EXPECT_TRUE(service->Load(&mock_store));
EXPECT_EQ(passphrase, service->passphrase_);
EXPECT_TRUE(service->connectable());
EXPECT_FALSE(service->need_passphrase_);
service->Unload();
EXPECT_EQ(string(""), service->passphrase_);
EXPECT_FALSE(service->connectable());
EXPECT_TRUE(service->need_passphrase_);
}
TEST_F(WiFiServiceTest, ConfigureMakesConnectable) {
string guid("legit_guid");
KeyValueStore args;
args.SetString(kEapIdentityProperty, "legit_identity");
args.SetString(kEapPasswordProperty, "legit_password");
args.SetString(kEapMethodProperty, "PEAP");
args.SetString(kGuidProperty, guid);
Error error;
WiFiServiceRefPtr service = MakeSimpleService(kSecurity8021x);
// Hack the GUID in so that we don't have to mess about with WiFi to regsiter
// our service. This way, Manager will handle the lookup itself.
service->SetGuid(guid, NULL);
manager()->RegisterService(service);
EXPECT_FALSE(service->connectable());
EXPECT_EQ(service.get(), manager()->GetService(args, &error).get());
EXPECT_TRUE(error.IsSuccess());
EXPECT_TRUE(service->connectable());
}
TEST_F(WiFiServiceTest, ConfigurePassphrase) {
EXPECT_EQ(Error::kNotSupported,
TestConfigurePassphrase(kSecurityNone, ""));
EXPECT_EQ(Error::kNotSupported,
TestConfigurePassphrase(kSecurityNone, "foo"));
EXPECT_EQ(Error::kSuccess,
TestConfigurePassphrase(kSecurityWep, NULL));
EXPECT_EQ(Error::kInvalidPassphrase,
TestConfigurePassphrase(kSecurityWep, ""));
EXPECT_EQ(Error::kInvalidPassphrase,
TestConfigurePassphrase(kSecurityWep, "abcd"));
EXPECT_EQ(Error::kSuccess,
TestConfigurePassphrase(kSecurityWep, "abcde"));
EXPECT_EQ(Error::kSuccess,
TestConfigurePassphrase(kSecurityWep, "abcdefghijklm"));
EXPECT_EQ(Error::kSuccess,
TestConfigurePassphrase(kSecurityWep, "0:abcdefghijklm"));
EXPECT_EQ(Error::kSuccess,
TestConfigurePassphrase(kSecurityWep, "0102030405"));
EXPECT_EQ(Error::kInvalidPassphrase,
TestConfigurePassphrase(kSecurityWep, "0x0102030405"));
EXPECT_EQ(Error::kInvalidPassphrase,
TestConfigurePassphrase(kSecurityWep, "O102030405"));
EXPECT_EQ(Error::kInvalidPassphrase,
TestConfigurePassphrase(kSecurityWep, "1:O102030405"));
EXPECT_EQ(Error::kInvalidPassphrase,
TestConfigurePassphrase(kSecurityWep, "1:0xO102030405"));
EXPECT_EQ(Error::kInvalidPassphrase,
TestConfigurePassphrase(kSecurityWep, "0xO102030405"));
EXPECT_EQ(Error::kSuccess,
TestConfigurePassphrase(kSecurityWep,
"0102030405060708090a0b0c0d"));
EXPECT_EQ(Error::kSuccess,
TestConfigurePassphrase(kSecurityWep,
"0102030405060708090A0B0C0D"));
EXPECT_EQ(Error::kSuccess,
TestConfigurePassphrase(kSecurityWep,
"0:0102030405060708090a0b0c0d"));
EXPECT_EQ(Error::kSuccess,
TestConfigurePassphrase(kSecurityWep,
"0:0x0102030405060708090a0b0c0d"));
EXPECT_EQ(Error::kSuccess,
TestConfigurePassphrase(kSecurityWpa, NULL));
EXPECT_EQ(Error::kSuccess,
TestConfigurePassphrase(kSecurityWpa, "secure password"));
EXPECT_EQ(Error::kInvalidPassphrase,
TestConfigurePassphrase(kSecurityWpa, ""));
EXPECT_EQ(Error::kSuccess, TestConfigurePassphrase(
kSecurityWpa,
string(IEEE_80211::kWPAAsciiMinLen, 'Z').c_str()));
EXPECT_EQ(Error::kSuccess, TestConfigurePassphrase(
kSecurityWpa,
string(IEEE_80211::kWPAAsciiMaxLen, 'Z').c_str()));
// subtle: invalid length for hex key, but valid as ascii passphrase
EXPECT_EQ(Error::kSuccess, TestConfigurePassphrase(
kSecurityWpa,
string(IEEE_80211::kWPAHexLen-1, '1').c_str()));
EXPECT_EQ(Error::kSuccess, TestConfigurePassphrase(
kSecurityWpa,
string(IEEE_80211::kWPAHexLen, '1').c_str()));
EXPECT_EQ(Error::kInvalidPassphrase, TestConfigurePassphrase(
kSecurityWpa,
string(IEEE_80211::kWPAAsciiMinLen-1, 'Z').c_str()));
EXPECT_EQ(Error::kInvalidPassphrase, TestConfigurePassphrase(
kSecurityWpa,
string(IEEE_80211::kWPAAsciiMaxLen+1, 'Z').c_str()));
EXPECT_EQ(Error::kInvalidPassphrase, TestConfigurePassphrase(
kSecurityWpa,
string(IEEE_80211::kWPAHexLen+1, '1').c_str()));
}
TEST_F(WiFiServiceTest, ConfigureRedundantProperties) {
WiFiServiceRefPtr service = MakeSimpleService(kSecurityNone);
KeyValueStore args;
args.SetString(kTypeProperty, kTypeWifi);
args.SetString(kSSIDProperty, simple_ssid_string());
args.SetString(kSecurityProperty, kSecurityNone);
args.SetString(kWifiHexSsid, "This is ignored even if it is invalid hex.");
const string kGUID = "aguid";
args.SetString(kGuidProperty, kGUID);
EXPECT_EQ("", service->guid());
Error error;
service->Configure(args, &error);
EXPECT_TRUE(error.IsSuccess());
EXPECT_EQ(kGUID, service->guid());
}
TEST_F(WiFiServiceTest, DisconnectWithWiFi) {
WiFiServiceRefPtr service = MakeServiceWithWiFi(kSecurityWep);
EXPECT_CALL(*wifi(), DisconnectFrom(service.get())).Times(1);
Error error;
service->Disconnect(&error);
}
TEST_F(WiFiServiceTest, DisconnectWithoutWiFi) {
WiFiServiceRefPtr service = MakeSimpleService(kSecurityWep);
EXPECT_CALL(*wifi(), DisconnectFrom(_)).Times(0);
Error error;
service->Disconnect(&error);
EXPECT_EQ(Error::kOperationFailed, error.type());
}
TEST_F(WiFiServiceTest, DisconnectWithoutWiFiWhileAssociating) {
WiFiServiceRefPtr service = MakeSimpleService(kSecurityWep);
EXPECT_CALL(*wifi(), DisconnectFrom(_)).Times(0);
service->SetState(Service::kStateAssociating);
ScopedMockLog log;
EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber());
EXPECT_CALL(log, Log(logging::LOG_ERROR, _,
HasSubstr("WiFi endpoints do not (yet) exist.")));
Error error;
service->Disconnect(&error);
EXPECT_EQ(Error::kOperationFailed, error.type());
}
TEST_F(WiFiServiceTest, UnloadAndClearCacheWEP) {
WiFiServiceRefPtr service = MakeServiceWithWiFi(kSecurityWep);
EXPECT_CALL(*wifi(), ClearCachedCredentials(service.get())).Times(1);
EXPECT_CALL(*wifi(), DisconnectFrom(service.get())).Times(1);
service->Unload();
}
TEST_F(WiFiServiceTest, UnloadAndClearCache8021x) {
WiFiServiceRefPtr service = MakeServiceWithWiFi(kSecurity8021x);
EXPECT_CALL(*wifi(), ClearCachedCredentials(service.get())).Times(1);
EXPECT_CALL(*wifi(), DisconnectFrom(service.get())).Times(1);
service->Unload();
}
TEST_F(WiFiServiceTest, ParseStorageIdentifierNone) {
WiFiServiceRefPtr service = MakeSimpleService(kSecurityNone);
const string storage_id = service->GetStorageIdentifier();
string address;
string mode;
string security;
EXPECT_TRUE(service->ParseStorageIdentifier(storage_id, &address, &mode,
&security));
EXPECT_EQ(StringToLowerASCII(GetAnyDeviceAddress()), address);
EXPECT_EQ(kModeManaged, mode);
EXPECT_EQ(kSecurityNone, security);
}
TEST_F(WiFiServiceTest, ParseStorageIdentifier8021x) {
// Do a separate test for 802.1x, since kSecurity8021x contains a "_",
// which needs to be dealt with specially in the parser.
WiFiServiceRefPtr service = MakeSimpleService(kSecurity8021x);
const string storage_id = service->GetStorageIdentifier();
string address;
string mode;
string security;
EXPECT_TRUE(service->ParseStorageIdentifier(storage_id, &address, &mode,
&security));
EXPECT_EQ(StringToLowerASCII(GetAnyDeviceAddress()), address);
EXPECT_EQ(kModeManaged, mode);
EXPECT_EQ(kSecurity8021x, security);
}
TEST_F(WiFiServiceFixupStorageTest, FixedEntries) {
const string kNonWiFiId = "vpn_foo";
const string kUnparsableWiFiId = "wifi_foo";
AddGroup(kNonWiFiId);
AddGroup(kUnparsableWiFiId);
AddServiceEntry(true, true, true, true);
AddServiceEntry(false, false, false, false);
AddServiceEntry(true, true, true, true);
AddServiceEntry(false, false, false, false);
EXPECT_TRUE(FixupServiceEntries());
}
TEST_F(WiFiServiceFixupStorageTest, NoFixedEntries) {
const string kNonWiFiId = "vpn_foo";
const string kUnparsableWiFiId = "wifi_foo";
AddGroup(kNonWiFiId);
AddGroup(kUnparsableWiFiId);
AddServiceEntry(true, true, true, true);
EXPECT_FALSE(FixupServiceEntries());
}
TEST_F(WiFiServiceFixupStorageTest, MissingTypeProperty) {
AddServiceEntry(false, true, true, true);
EXPECT_TRUE(FixupServiceEntries());
}
TEST_F(WiFiServiceFixupStorageTest, MissingModeProperty) {
AddServiceEntry(true, false, true, true);
EXPECT_TRUE(FixupServiceEntries());
}
TEST_F(WiFiServiceFixupStorageTest, MissingSecurityProperty) {
AddServiceEntry(true, true, false, true);
EXPECT_TRUE(FixupServiceEntries());
}
TEST_F(WiFiServiceFixupStorageTest, MissingSecurityClassProperty) {
AddServiceEntry(true, true, true, false);
EXPECT_TRUE(FixupServiceEntries());
}
TEST_F(WiFiServiceTest, Connectable) {
// Open network should be connectable.
EXPECT_TRUE(CheckConnectable(kSecurityNone, NULL, false));
// Open network should remain connectable if we try to set a password on it.
EXPECT_TRUE(CheckConnectable(kSecurityNone, "abcde", false));
// WEP network with passphrase set should be connectable.
EXPECT_TRUE(CheckConnectable(kSecurityWep, "abcde", false));
// WEP network without passphrase set should NOT be connectable.
EXPECT_FALSE(CheckConnectable(kSecurityWep, NULL, false));
// A bad passphrase should not make a WEP network connectable.
EXPECT_FALSE(CheckConnectable(kSecurityWep, "a", false));
// Similar to WEP, for WPA.
EXPECT_TRUE(CheckConnectable(kSecurityWpa, "abcdefgh", false));
EXPECT_FALSE(CheckConnectable(kSecurityWpa, NULL, false));
EXPECT_FALSE(CheckConnectable(kSecurityWpa, "a", false));
// 802.1x without connectable EAP credentials should NOT be connectable.
EXPECT_FALSE(CheckConnectable(kSecurity8021x, NULL, false));
// 802.1x with connectable EAP credentials should be connectable.
EXPECT_TRUE(CheckConnectable(kSecurity8021x, NULL, true));
// Dynamic WEP + 802.1X should be connectable under the same conditions.
EXPECT_TRUE(CheckConnectable(kSecurityWep, NULL, true));
}
TEST_F(WiFiServiceTest, IsAutoConnectable) {
const char *reason;
WiFiServiceRefPtr service = MakeSimpleService(kSecurityNone);
EXPECT_CALL(*wifi(), IsIdle())
.WillRepeatedly(Return(true));
EXPECT_FALSE(service->HasEndpoints());
EXPECT_FALSE(service->IsAutoConnectable(&reason));
EXPECT_STREQ(WiFiService::kAutoConnNoEndpoint, reason);
reason = "";
WiFiEndpointRefPtr endpoint =
MakeOpenEndpoint("a", "00:00:00:00:00:01", 0, 0);
service->AddEndpoint(endpoint);
EXPECT_CALL(*wifi(), IsIdle())
.WillRepeatedly(Return(true));
EXPECT_TRUE(service->HasEndpoints());
EXPECT_TRUE(service->IsAutoConnectable(&reason));
EXPECT_STREQ("", reason);
// WiFi only supports connecting to one Service at a time. So, to
// avoid disrupting connectivity, we only allow auto-connection to
// a WiFiService when the corresponding WiFi is idle.
EXPECT_CALL(*wifi(), IsIdle())
.WillRepeatedly(Return(false));
EXPECT_TRUE(service->HasEndpoints());
EXPECT_FALSE(service->IsAutoConnectable(&reason));
EXPECT_STREQ(WiFiService::kAutoConnBusy, reason);
}
TEST_F(WiFiServiceTest, AutoConnect) {
const char *reason;
WiFiServiceRefPtr service = MakeSimpleService(kSecurityNone);
EXPECT_FALSE(service->IsAutoConnectable(&reason));
EXPECT_CALL(*wifi(), ConnectTo(_)).Times(0);
service->AutoConnect();
dispatcher()->DispatchPendingEvents();
WiFiEndpointRefPtr endpoint =
MakeOpenEndpoint("a", "00:00:00:00:00:01", 0, 0);
service->AddEndpoint(endpoint);
EXPECT_CALL(*wifi(), IsIdle())
.WillRepeatedly(Return(true));
EXPECT_TRUE(service->IsAutoConnectable(&reason));
EXPECT_CALL(*wifi(), ConnectTo(_));
service->AutoConnect();
dispatcher()->DispatchPendingEvents();
Error error;
service->UserInitiatedDisconnect(&error);
dispatcher()->DispatchPendingEvents();
EXPECT_FALSE(service->IsAutoConnectable(&reason));
}
TEST_F(WiFiServiceTest, ClearWriteOnlyDerivedProperty) {
WiFiServiceRefPtr wifi_service = MakeSimpleService(kSecurityWep);
EXPECT_EQ("", wifi_service->passphrase_);
::DBus::Error error;
EXPECT_TRUE(DBusAdaptor::SetProperty(
wifi_service->mutable_store(),
kPassphraseProperty,
DBusAdaptor::StringToVariant("0:abcde"),
&error));
EXPECT_EQ("0:abcde", wifi_service->passphrase_);
EXPECT_TRUE(DBusAdaptor::ClearProperty(wifi_service->mutable_store(),
kPassphraseProperty,
&error));
EXPECT_EQ("", wifi_service->passphrase_);
}
TEST_F(WiFiServiceTest, SignalToStrength) {
// Verify that our mapping is sane, in the sense that it preserves ordering.
// We break the test into two domains, because we assume that positive
// values aren't actually in dBm.
for (int16 i = std::numeric_limits<int16>::min(); i < 0; ++i) {
int16 current_mapped = WiFiService::SignalToStrength(i);
int16 next_mapped = WiFiService::SignalToStrength(i+1);
EXPECT_LE(current_mapped, next_mapped)
<< "(original values " << i << " " << i+1 << ")";
EXPECT_GE(current_mapped, Service::kStrengthMin);
EXPECT_LE(current_mapped, Service::kStrengthMax);
}
for (int16 i = 1; i < std::numeric_limits<int16>::max(); ++i) {
int16 current_mapped = WiFiService::SignalToStrength(i);
int16 next_mapped = WiFiService::SignalToStrength(i+1);
EXPECT_LE(current_mapped, next_mapped)
<< "(original values " << i << " " << i+1 << ")";
EXPECT_GE(current_mapped, Service::kStrengthMin);
EXPECT_LE(current_mapped, Service::kStrengthMax);
}
}
TEST_F(WiFiServiceUpdateFromEndpointsTest, Strengths) {
// If the chosen signal values don't map to distinct strength
// values, then we can't expect our other tests to pass. So verify
// their distinctness.
EXPECT_TRUE(kOkEndpointStrength != kBadEndpointStrength);
EXPECT_TRUE(kOkEndpointStrength != kGoodEndpointStrength);
EXPECT_TRUE(kGoodEndpointStrength != kBadEndpointStrength);
}
TEST_F(WiFiServiceUpdateFromEndpointsTest, Floating) {
// Initial endpoint updates values.
EXPECT_CALL(adaptor, EmitUint16Changed(kWifiFrequency, kOkEndpointFrequency));
EXPECT_CALL(adaptor, EmitStringChanged(kWifiBSsid, kOkEndpointBssId));
EXPECT_CALL(adaptor,
EmitUint8Changed(kSignalStrengthProperty, kOkEndpointStrength));
EXPECT_CALL(adaptor,
EmitUint16Changed(kWifiPhyMode, Metrics::kWiFiNetworkPhyMode11b));
service->AddEndpoint(ok_endpoint);
EXPECT_EQ(1, service->GetEndpointCount());
Mock::VerifyAndClearExpectations(&adaptor);
// Endpoint with stronger signal updates values.
EXPECT_CALL(adaptor,
EmitUint16Changed(kWifiFrequency, kGoodEndpointFrequency));
EXPECT_CALL(adaptor, EmitStringChanged(kWifiBSsid, kGoodEndpointBssId));
EXPECT_CALL(adaptor,
EmitUint8Changed(kSignalStrengthProperty, kGoodEndpointStrength));
// However, both endpoints are 11b.
EXPECT_CALL(adaptor, EmitUint16Changed(kWifiPhyMode, _)).Times(0);
service->AddEndpoint(good_endpoint);
EXPECT_EQ(2, service->GetEndpointCount());
Mock::VerifyAndClearExpectations(&adaptor);
// Endpoint with lower signal does not change values.
EXPECT_CALL(adaptor, EmitUint16Changed(kWifiFrequency, _)).Times(0);
EXPECT_CALL(adaptor, EmitStringChanged(kWifiBSsid, _)).Times(0);
EXPECT_CALL(adaptor,
EmitUint8Changed(kSignalStrengthProperty, _)).Times(0);
EXPECT_CALL(adaptor, EmitUint16Changed(kWifiPhyMode, _)).Times(0);
service->AddEndpoint(bad_endpoint);
EXPECT_EQ(3, service->GetEndpointCount());
Mock::VerifyAndClearExpectations(&adaptor);
// Removing non-optimal endpoint does not change values.
EXPECT_CALL(adaptor, EmitUint16Changed(kWifiFrequency, _)).Times(0);
EXPECT_CALL(adaptor, EmitStringChanged(kWifiBSsid, _)).Times(0);
EXPECT_CALL(adaptor,
EmitUint8Changed(kSignalStrengthProperty, _)).Times(0);
EXPECT_CALL(adaptor, EmitUint16Changed(kWifiPhyMode, _)).Times(0);
service->RemoveEndpoint(bad_endpoint);
EXPECT_EQ(2, service->GetEndpointCount());
Mock::VerifyAndClearExpectations(&adaptor);
// Removing optimal endpoint updates values.
EXPECT_CALL(adaptor, EmitUint16Changed(kWifiFrequency, kOkEndpointFrequency));
EXPECT_CALL(adaptor, EmitStringChanged(kWifiBSsid, kOkEndpointBssId));
EXPECT_CALL(adaptor,
EmitUint8Changed(kSignalStrengthProperty, kOkEndpointStrength));
// However, both endpoints are 11b.
EXPECT_CALL(adaptor, EmitUint16Changed(kWifiPhyMode, _)).Times(0);
service->RemoveEndpoint(good_endpoint);
EXPECT_EQ(1, service->GetEndpointCount());
Mock::VerifyAndClearExpectations(&adaptor);
// Removing last endpoint updates values (and doesn't crash).
EXPECT_CALL(adaptor, EmitUint16Changed(kWifiFrequency, _));
EXPECT_CALL(adaptor, EmitStringChanged(kWifiBSsid, _));
EXPECT_CALL(adaptor, EmitUint8Changed(kSignalStrengthProperty, _));
EXPECT_CALL(adaptor, EmitUint16Changed(
kWifiPhyMode, Metrics::kWiFiNetworkPhyModeUndef));
service->RemoveEndpoint(ok_endpoint);
EXPECT_EQ(0, service->GetEndpointCount());
Mock::VerifyAndClearExpectations(&adaptor);
}
TEST_F(WiFiServiceUpdateFromEndpointsTest, Connected) {
EXPECT_CALL(adaptor, EmitUint16Changed(_, _)).Times(AnyNumber());
EXPECT_CALL(adaptor, EmitStringChanged(_, _)).Times(AnyNumber());
EXPECT_CALL(adaptor, EmitUint8Changed(_, _)).Times(AnyNumber());
EXPECT_CALL(adaptor, EmitBoolChanged(_, _)).Times(AnyNumber());
service->AddEndpoint(bad_endpoint);
service->AddEndpoint(ok_endpoint);
EXPECT_EQ(2, service->GetEndpointCount());
Mock::VerifyAndClearExpectations(&adaptor);
// Setting current endpoint forces adoption of its values, even if it
// doesn't have the highest signal.
EXPECT_CALL(adaptor,
EmitUint16Changed(kWifiFrequency, kBadEndpointFrequency));
EXPECT_CALL(adaptor, EmitStringChanged(kWifiBSsid, kBadEndpointBssId));
EXPECT_CALL(adaptor,
EmitUint8Changed(kSignalStrengthProperty, kBadEndpointStrength));
service->NotifyCurrentEndpoint(bad_endpoint);
Mock::VerifyAndClearExpectations(&adaptor);
// Adding a better endpoint doesn't matter, when current endpoint is set.
EXPECT_CALL(adaptor, EmitUint16Changed(kWifiFrequency, _)).Times(0);
EXPECT_CALL(adaptor, EmitStringChanged(kWifiBSsid, _)).Times(0);
EXPECT_CALL(adaptor,
EmitUint8Changed(kSignalStrengthProperty, _)).Times(0);
service->AddEndpoint(good_endpoint);
EXPECT_EQ(3, service->GetEndpointCount());
Mock::VerifyAndClearExpectations(&adaptor);
// Removing a better endpoint doesn't matter, when current endpoint is set.
EXPECT_CALL(adaptor, EmitUint16Changed(kWifiFrequency, _)).Times(0);
EXPECT_CALL(adaptor, EmitStringChanged(kWifiBSsid, _)).Times(0);
EXPECT_CALL(adaptor,
EmitUint8Changed(kSignalStrengthProperty, _)).Times(0);
service->RemoveEndpoint(good_endpoint);
Mock::VerifyAndClearExpectations(&adaptor);
// Removing the current endpoint is safe and sane.
EXPECT_CALL(adaptor, EmitUint16Changed(kWifiFrequency, kOkEndpointFrequency));
EXPECT_CALL(adaptor, EmitStringChanged(kWifiBSsid, kOkEndpointBssId));
EXPECT_CALL(adaptor,
EmitUint8Changed(kSignalStrengthProperty, kOkEndpointStrength));
service->RemoveEndpoint(bad_endpoint);
Mock::VerifyAndClearExpectations(&adaptor);
// Clearing the current endpoint (without removing it) is also safe and sane.
service->NotifyCurrentEndpoint(ok_endpoint);
EXPECT_CALL(adaptor, EmitUint16Changed(kWifiFrequency, _)).Times(0);
EXPECT_CALL(adaptor, EmitStringChanged(kWifiBSsid, _)).Times(0);
EXPECT_CALL(adaptor,
EmitUint8Changed(kSignalStrengthProperty, _)).Times(0);
service->NotifyCurrentEndpoint(NULL);
Mock::VerifyAndClearExpectations(&adaptor);
}
TEST_F(WiFiServiceUpdateFromEndpointsTest, EndpointModified) {
EXPECT_CALL(adaptor, EmitUint16Changed(_, _)).Times(AnyNumber());
EXPECT_CALL(adaptor, EmitStringChanged(_, _)).Times(AnyNumber());
EXPECT_CALL(adaptor, EmitUint8Changed(_, _)).Times(AnyNumber());
EXPECT_CALL(adaptor, EmitBoolChanged(_, _)).Times(AnyNumber());
service->AddEndpoint(ok_endpoint);
service->AddEndpoint(good_endpoint);
EXPECT_EQ(2, service->GetEndpointCount());
Mock::VerifyAndClearExpectations(&adaptor);
// Updating sub-optimal Endpoint doesn't update Service.
EXPECT_CALL(adaptor, EmitUint16Changed(kWifiFrequency, _)).Times(0);
EXPECT_CALL(adaptor, EmitStringChanged(kWifiBSsid, _)).Times(0);
EXPECT_CALL(adaptor,
EmitUint8Changed(kSignalStrengthProperty, _)).Times(0);
ok_endpoint->signal_strength_ = (kOkEndpointSignal + kGoodEndpointSignal) / 2;
service->NotifyEndpointUpdated(ok_endpoint);
Mock::VerifyAndClearExpectations(&adaptor);
// Updating optimal Endpoint updates appropriate Service property.
EXPECT_CALL(adaptor, EmitUint16Changed(kWifiFrequency, _)).Times(0);
EXPECT_CALL(adaptor, EmitStringChanged(kWifiBSsid, _)).Times(0);
EXPECT_CALL(adaptor, EmitUint8Changed(kSignalStrengthProperty, _));
good_endpoint->signal_strength_ = kGoodEndpointSignal + 1;
service->NotifyEndpointUpdated(good_endpoint);
Mock::VerifyAndClearExpectations(&adaptor);
// Change in optimal Endpoint updates Service properties.
EXPECT_CALL(adaptor, EmitUint16Changed(kWifiFrequency, kOkEndpointFrequency));
EXPECT_CALL(adaptor, EmitStringChanged(kWifiBSsid, kOkEndpointBssId));
EXPECT_CALL(adaptor, EmitUint8Changed(kSignalStrengthProperty, _));
ok_endpoint->signal_strength_ = kGoodEndpointSignal + 2;
service->NotifyEndpointUpdated(ok_endpoint);
Mock::VerifyAndClearExpectations(&adaptor);
}
TEST_F(WiFiServiceUpdateFromEndpointsTest, Ieee80211w) {
EXPECT_CALL(adaptor, EmitUint16Changed(_, _)).Times(AnyNumber());
EXPECT_CALL(adaptor, EmitStringChanged(_, _)).Times(AnyNumber());
EXPECT_CALL(adaptor, EmitUint8Changed(_, _)).Times(AnyNumber());
EXPECT_CALL(adaptor, EmitBoolChanged(_, _)).Times(AnyNumber());
service->AddEndpoint(ok_endpoint);
EXPECT_FALSE(service->ieee80211w_required());
good_endpoint->ieee80211w_required_ = true;
service->AddEndpoint(good_endpoint);
EXPECT_TRUE(service->ieee80211w_required());
service->RemoveEndpoint(good_endpoint);
EXPECT_TRUE(service->ieee80211w_required());
}
TEST_F(WiFiServiceUpdateFromEndpointsTest, PhysicalMode) {
EXPECT_CALL(adaptor, EmitUint16Changed(_, _)).Times(AnyNumber());
EXPECT_CALL(adaptor, EmitStringChanged(_, _)).Times(AnyNumber());
EXPECT_CALL(adaptor, EmitUint8Changed(_, _)).Times(AnyNumber());
EXPECT_CALL(adaptor, EmitBoolChanged(_, _)).Times(AnyNumber());
// No endpoints -> undef.
EXPECT_EQ(Metrics::kWiFiNetworkPhyModeUndef, service->physical_mode());
// Endpoint has unknown physical mode -> undef.
ok_endpoint->physical_mode_ = Metrics::kWiFiNetworkPhyModeUndef;
service->AddEndpoint(ok_endpoint);
EXPECT_EQ(Metrics::kWiFiNetworkPhyModeUndef, service->physical_mode());
// New endpoint with 802.11a -> 802.11a.
good_endpoint->physical_mode_ = Metrics::kWiFiNetworkPhyMode11a;
service->AddEndpoint(good_endpoint);
EXPECT_EQ(Metrics::kWiFiNetworkPhyMode11a, service->physical_mode());
// Remove 802.11a endpoint -> undef.
service->RemoveEndpoint(good_endpoint);
EXPECT_EQ(Metrics::kWiFiNetworkPhyModeUndef, service->physical_mode());
// Change endpoint -> take endpoint's new value.
ok_endpoint->physical_mode_ = Metrics::kWiFiNetworkPhyMode11n;
service->NotifyEndpointUpdated(ok_endpoint);
EXPECT_EQ(Metrics::kWiFiNetworkPhyMode11n, service->physical_mode());
// No endpoints -> undef.
service->RemoveEndpoint(ok_endpoint);
EXPECT_EQ(Metrics::kWiFiNetworkPhyModeUndef, service->physical_mode());
}
TEST_F(WiFiServiceUpdateFromEndpointsTest, WarningOnDisconnect) {
service->AddEndpoint(ok_endpoint);
service->SetState(Service::kStateAssociating);
ScopedMockLog log;
EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber());
EXPECT_CALL(log, Log(logging::LOG_WARNING, _,
EndsWith("disconnect due to no remaining endpoints.")));
service->RemoveEndpoint(ok_endpoint);
}
MATCHER_P(IsSetwiseEqual, expected_set, "") {
set<uint16> arg_set(arg.begin(), arg.end());
return arg_set == expected_set;
}
TEST_F(WiFiServiceUpdateFromEndpointsTest, FrequencyList) {
EXPECT_CALL(adaptor, EmitUint16Changed(_, _)).Times(AnyNumber());
EXPECT_CALL(adaptor, EmitStringChanged(_, _)).Times(AnyNumber());
EXPECT_CALL(adaptor, EmitUint8Changed(_, _)).Times(AnyNumber());
EXPECT_CALL(adaptor, EmitBoolChanged(_, _)).Times(AnyNumber());
// No endpoints -> empty list.
EXPECT_EQ(vector<uint16>(), service->frequency_list());
// Add endpoint -> endpoint's frequency in list.
EXPECT_CALL(adaptor, EmitUint16sChanged(
kWifiFrequencyListProperty, vector<uint16>{kGoodEndpointFrequency}));
service->AddEndpoint(good_endpoint);
Mock::VerifyAndClearExpectations(&adaptor);
// Add another endpoint -> both frequencies in list.
// Order doesn't matter.
set<uint16> expected_frequencies{kGoodEndpointFrequency,
kOkEndpointFrequency};
EXPECT_CALL(adaptor, EmitUint16sChanged(
kWifiFrequencyListProperty, IsSetwiseEqual(expected_frequencies)));
service->AddEndpoint(ok_endpoint);
Mock::VerifyAndClearExpectations(&adaptor);
// Remove endpoint -> other endpoint's frequency remains.
EXPECT_CALL(adaptor, EmitUint16sChanged(
kWifiFrequencyListProperty, vector<uint16>{kOkEndpointFrequency}));
service->RemoveEndpoint(good_endpoint);
Mock::VerifyAndClearExpectations(&adaptor);
// Endpoint with same frequency -> frequency remains.
// Notification may or may not occur -- don't care.
// Frequency may or may not be repeated in list -- don't care.
WiFiEndpointRefPtr same_freq_as_ok_endpoint = MakeOpenEndpoint(
simple_ssid_string(), "aa:bb:cc:dd:ee:ff", ok_endpoint->frequency(), 0);
service->AddEndpoint(same_freq_as_ok_endpoint);
EXPECT_THAT(service->frequency_list(),
IsSetwiseEqual(set<uint16>{kOkEndpointFrequency}));
Mock::VerifyAndClearExpectations(&adaptor);
// Remove endpoint with same frequency -> frequency remains.
// Notification may or may not occur -- don't care.
service->RemoveEndpoint(ok_endpoint);
EXPECT_EQ(vector<uint16>{same_freq_as_ok_endpoint->frequency()},
service->frequency_list());
Mock::VerifyAndClearExpectations(&adaptor);
// Remove last endpoint. Frequency list goes empty.
EXPECT_CALL(adaptor, EmitUint16sChanged(
kWifiFrequencyListProperty, vector<uint16>{}));
service->RemoveEndpoint(same_freq_as_ok_endpoint);
Mock::VerifyAndClearExpectations(&adaptor);
}
TEST_F(WiFiServiceTest, SecurityFromCurrentEndpoint) {
WiFiServiceRefPtr service(MakeSimpleService(kSecurityPsk));
EXPECT_EQ(kSecurityPsk, service->GetSecurity(NULL));
WiFiEndpoint *endpoint = MakeOpenEndpoint(
simple_ssid_string(), "00:00:00:00:00:00", 0, 0);
service->AddEndpoint(endpoint);
EXPECT_EQ(kSecurityPsk, service->GetSecurity(NULL));
service->NotifyCurrentEndpoint(endpoint);
EXPECT_EQ(kSecurityNone, service->GetSecurity(NULL));
service->NotifyCurrentEndpoint(NULL);
EXPECT_EQ(kSecurityPsk, service->GetSecurity(NULL));
}
TEST_F(WiFiServiceTest, UpdateSecurity) {
// Cleartext and pre-shared-key crypto.
{
WiFiServiceRefPtr service = MakeSimpleService(kSecurityNone);
EXPECT_EQ(Service::kCryptoNone, service->crypto_algorithm());
EXPECT_FALSE(service->key_rotation());
EXPECT_FALSE(service->endpoint_auth());
}
{
WiFiServiceRefPtr service = MakeSimpleService(kSecurityWep);
EXPECT_EQ(Service::kCryptoRc4, service->crypto_algorithm());
EXPECT_FALSE(service->key_rotation());
EXPECT_FALSE(service->endpoint_auth());
}
{
WiFiServiceRefPtr service = MakeSimpleService(kSecurityPsk);
EXPECT_EQ(Service::kCryptoRc4, service->crypto_algorithm());
EXPECT_TRUE(service->key_rotation());
EXPECT_FALSE(service->endpoint_auth());
}
{
WiFiServiceRefPtr service = MakeSimpleService(kSecurityWpa);
EXPECT_EQ(Service::kCryptoRc4, service->crypto_algorithm());
EXPECT_TRUE(service->key_rotation());
EXPECT_FALSE(service->endpoint_auth());
}
{
WiFiServiceRefPtr service = MakeSimpleService(kSecurityRsn);
EXPECT_EQ(Service::kCryptoAes, service->crypto_algorithm());
EXPECT_TRUE(service->key_rotation());
EXPECT_FALSE(service->endpoint_auth());
}
// Crypto with 802.1X key management.
{
// WEP
WiFiServiceRefPtr service = MakeSimpleService(kSecurityWep);
service->SetEAPKeyManagement("IEEE8021X");
EXPECT_EQ(Service::kCryptoRc4, service->crypto_algorithm());
EXPECT_TRUE(service->key_rotation());
EXPECT_TRUE(service->endpoint_auth());
}
{
// WPA
WiFiServiceRefPtr service = MakeSimpleService(kSecurity8021x);
WiFiEndpointRefPtr endpoint =
MakeEndpoint("a", "00:00:00:00:00:01", 0, 0, true, false);
service->AddEndpoint(endpoint);
EXPECT_EQ(Service::kCryptoRc4, service->crypto_algorithm());
EXPECT_TRUE(service->key_rotation());
EXPECT_TRUE(service->endpoint_auth());
}
{
// RSN
WiFiServiceRefPtr service = MakeSimpleService(kSecurity8021x);
WiFiEndpointRefPtr endpoint =
MakeEndpoint("a", "00:00:00:00:00:01", 0, 0, false, true);
service->AddEndpoint(endpoint);
EXPECT_EQ(Service::kCryptoAes, service->crypto_algorithm());
EXPECT_TRUE(service->key_rotation());
EXPECT_TRUE(service->endpoint_auth());
}
{
// AP supports both WPA and RSN.
WiFiServiceRefPtr service = MakeSimpleService(kSecurity8021x);
WiFiEndpointRefPtr endpoint =
MakeEndpoint("a", "00:00:00:00:00:01", 0, 0, true, true);
service->AddEndpoint(endpoint);
EXPECT_EQ(Service::kCryptoAes, service->crypto_algorithm());
EXPECT_TRUE(service->key_rotation());
EXPECT_TRUE(service->endpoint_auth());
}
}
TEST_F(WiFiServiceTest, ComputeCipher8021x) {
// No endpoints.
{
const set<WiFiEndpointConstRefPtr> endpoints;
EXPECT_EQ(Service::kCryptoNone,
WiFiService::ComputeCipher8021x(endpoints));
}
// Single endpoint, various configs.
{
set<WiFiEndpointConstRefPtr> endpoints;
endpoints.insert(
MakeEndpoint("a", "00:00:00:00:00:01", 0, 0, false, false));
EXPECT_EQ(Service::kCryptoNone,
WiFiService::ComputeCipher8021x(endpoints));
}
{
set<WiFiEndpointConstRefPtr> endpoints;
endpoints.insert(
MakeEndpoint("a", "00:00:00:00:00:01", 0, 0, true, false));
EXPECT_EQ(Service::kCryptoRc4,
WiFiService::ComputeCipher8021x(endpoints));
}
{
set<WiFiEndpointConstRefPtr> endpoints;
endpoints.insert(
MakeEndpoint("a", "00:00:00:00:00:01", 0, 0, false, true));
EXPECT_EQ(Service::kCryptoAes,
WiFiService::ComputeCipher8021x(endpoints));
}
{
set<WiFiEndpointConstRefPtr> endpoints;
endpoints.insert(
MakeEndpoint("a", "00:00:00:00:00:01", 0, 0, true, true));
EXPECT_EQ(Service::kCryptoAes,
WiFiService::ComputeCipher8021x(endpoints));
}
// Multiple endpoints.
{
set<WiFiEndpointConstRefPtr> endpoints;
endpoints.insert(
MakeEndpoint("a", "00:00:00:00:00:01", 0, 0, false, false));
endpoints.insert(
MakeEndpoint("a", "00:00:00:00:00:02", 0, 0, false, false));
EXPECT_EQ(Service::kCryptoNone,
WiFiService::ComputeCipher8021x(endpoints));
}
{
set<WiFiEndpointConstRefPtr> endpoints;
endpoints.insert(
MakeEndpoint("a", "00:00:00:00:00:01", 0, 0, false, false));
endpoints.insert(
MakeEndpoint("a", "00:00:00:00:00:02", 0, 0, true, false));
EXPECT_EQ(Service::kCryptoNone,
WiFiService::ComputeCipher8021x(endpoints));
}
{
set<WiFiEndpointConstRefPtr> endpoints;
endpoints.insert(
MakeEndpoint("a", "00:00:00:00:00:01", 0, 0, true, false));
endpoints.insert(
MakeEndpoint("a", "00:00:00:00:00:02", 0, 0, true, false));
EXPECT_EQ(Service::kCryptoRc4,
WiFiService::ComputeCipher8021x(endpoints));
}
{
set<WiFiEndpointConstRefPtr> endpoints;
endpoints.insert(
MakeEndpoint("a", "00:00:00:00:00:01", 0, 0, true, false));
endpoints.insert(
MakeEndpoint("a", "00:00:00:00:00:02", 0, 0, false, true));
EXPECT_EQ(Service::kCryptoRc4,
WiFiService::ComputeCipher8021x(endpoints));
}
{
set<WiFiEndpointConstRefPtr> endpoints;
endpoints.insert(
MakeEndpoint("a", "00:00:00:00:00:01", 0, 0, false, true));
endpoints.insert(
MakeEndpoint("a", "00:00:00:00:00:02", 0, 0, false, true));
EXPECT_EQ(Service::kCryptoAes,
WiFiService::ComputeCipher8021x(endpoints));
}
{
set<WiFiEndpointConstRefPtr> endpoints;
endpoints.insert(
MakeEndpoint("a", "00:00:00:00:00:01", 0, 0, true, true));
endpoints.insert(
MakeEndpoint("a", "00:00:00:00:00:02", 0, 0, true, true));
EXPECT_EQ(Service::kCryptoAes,
WiFiService::ComputeCipher8021x(endpoints));
}
}
TEST_F(WiFiServiceTest, Unload) {
WiFiServiceRefPtr service = MakeServiceWithWiFi(kSecurityNone);
EXPECT_CALL(*wifi(), DestroyIPConfigLease(service->GetStorageIdentifier())).
Times(1);
service->Unload();
}
TEST_F(WiFiServiceTest, PropertyChanges) {
WiFiServiceRefPtr service = MakeServiceWithMockManager();
ServiceMockAdaptor *adaptor = GetAdaptor(service);
TestCommonPropertyChanges(service, adaptor);
TestAutoConnectPropertyChange(service, adaptor);
EXPECT_CALL(*adaptor,
EmitRpcIdentifierChanged(kDeviceProperty, _));
SetWiFi(service, wifi());
Mock::VerifyAndClearExpectations(adaptor);
EXPECT_CALL(*adaptor,
EmitRpcIdentifierChanged(kDeviceProperty, _));
service->ResetWiFi();
Mock::VerifyAndClearExpectations(adaptor);
}
// Custom property setters should return false, and make no changes, if
// the new value is the same as the old value.
TEST_F(WiFiServiceTest, CustomSetterNoopChange) {
WiFiServiceRefPtr service = MakeServiceWithMockManager();
TestCustomSetterNoopChange(service, mock_manager());
}
TEST_F(WiFiServiceTest, SuspectedCredentialFailure) {
WiFiServiceRefPtr service = MakeSimpleService(kSecurityWpa);
EXPECT_FALSE(service->has_ever_connected());
EXPECT_EQ(0, service->suspected_credential_failures_);
EXPECT_TRUE(service->AddSuspectedCredentialFailure());
EXPECT_EQ(0, service->suspected_credential_failures_);
service->has_ever_connected_ = true;
for (int i = 0; i < WiFiService::kSuspectedCredentialFailureThreshold - 1;
++i) {
EXPECT_FALSE(service->AddSuspectedCredentialFailure());
EXPECT_EQ(i + 1, service->suspected_credential_failures_);
}
EXPECT_TRUE(service->AddSuspectedCredentialFailure());
// Make sure the failure state does not reset just because we ask again.
EXPECT_TRUE(service->AddSuspectedCredentialFailure());
// Make sure the failure state does not reset because of a credential change.
Error error;
service->SetPassphrase("Panchromatic Resonance", &error);
EXPECT_TRUE(error.IsSuccess());
EXPECT_TRUE(service->AddSuspectedCredentialFailure());
service->ResetSuspectedCredentialFailures();
EXPECT_EQ(0, service->suspected_credential_failures_);
EXPECT_FALSE(service->AddSuspectedCredentialFailure());
}
TEST_F(WiFiServiceTest, GetTethering) {
WiFiServiceRefPtr service = MakeSimpleService(kSecurityNone);
EXPECT_EQ(kTetheringNotDetectedState, service->GetTethering(NULL));
// Since the device isn't connected, we shouldn't even query the WiFi device.
EXPECT_CALL(*wifi(), IsConnectedViaTether()).Times(0);
SetWiFiForService(service, wifi());
EXPECT_EQ(kTetheringNotDetectedState, service->GetTethering(NULL));
Mock::VerifyAndClearExpectations(wifi());
scoped_refptr<MockProfile> mock_profile(
new NiceMock<MockProfile>(control_interface(), metrics(), manager()));
service->set_profile(mock_profile);
service->SetState(Service::kStateConnected);
// A connected service should return "confirmed" iff the underlying device
// reports it is tethered.
EXPECT_CALL(*wifi(), IsConnectedViaTether())
.WillOnce(Return(true))
.WillOnce(Return(false));
EXPECT_EQ(kTetheringConfirmedState, service->GetTethering(NULL));
EXPECT_EQ(kTetheringNotDetectedState, service->GetTethering(NULL));
Mock::VerifyAndClearExpectations(wifi());
// Add two endpoints that have a BSSID associated with some Android devices
// in tethering mode.
WiFiEndpointRefPtr endpoint_android1 =
MakeOpenEndpoint("a", "02:1a:11:00:00:01", 2412, 0);
service->AddEndpoint(endpoint_android1);
WiFiEndpointRefPtr endpoint_android2 =
MakeOpenEndpoint("a", "02:1a:11:00:00:02", 2412, 0);
service->AddEndpoint(endpoint_android2);
// Since there are two endpoints, we should not detect tethering mode.
EXPECT_CALL(*wifi(), IsConnectedViaTether()).WillOnce(Return(false));
EXPECT_EQ(kTetheringNotDetectedState, service->GetTethering(NULL));
// If the device reports that it is tethered, this should override any
// findings gained from examining the endpoints.
EXPECT_CALL(*wifi(), IsConnectedViaTether()).WillOnce(Return(true));
EXPECT_EQ(kTetheringConfirmedState, service->GetTethering(NULL));
// Continue in the un-tethered device case for a few more tests below.
Mock::VerifyAndClearExpectations(wifi());
EXPECT_CALL(*wifi(), IsConnectedViaTether())
.WillRepeatedly(Return(false));
// Removing an endpoint so we only have one should put us in the "Suspected"
// state.
service->RemoveEndpoint(endpoint_android1);
EXPECT_EQ(kTetheringSuspectedState, service->GetTethering(NULL));
// Add a different endpoint which has a locally administered MAC address
// but not one used by Android.
service->RemoveEndpoint(endpoint_android2);
WiFiEndpointRefPtr endpoint_ios =
MakeOpenEndpoint("a", "02:00:00:00:00:01", 2412, 0);
service->AddEndpoint(endpoint_ios);
EXPECT_EQ(kTetheringNotDetectedState, service->GetTethering(NULL));
// If this endpoint reports the right vendor OUI, we should suspect
// it to be tethered. However since this evaluation normally only
// happens in the endpoint constructor, we must force it to recalculate.
endpoint_ios->vendor_information_.oui_set.insert(Tethering::kIosOui);
endpoint_ios->CheckForTetheringSignature();
EXPECT_EQ(kTetheringSuspectedState, service->GetTethering(NULL));
// If the device reports that it is tethered, this should override any
// findings gained from examining the endpoints.
Mock::VerifyAndClearExpectations(wifi());
EXPECT_CALL(*wifi(), IsConnectedViaTether()).WillOnce(Return(true));
EXPECT_EQ(kTetheringConfirmedState, service->GetTethering(NULL));
}
TEST_F(WiFiServiceTest, IsVisible) {
WiFiServiceRefPtr wifi_service = MakeServiceWithWiFi(kSecurityNone);
ServiceMockAdaptor *adaptor = GetAdaptor(wifi_service);
// Adding the first endpoint emits a change: Visible = true.
EXPECT_CALL(*adaptor, EmitBoolChanged(kVisibleProperty, true));
WiFiEndpointRefPtr endpoint =
MakeOpenEndpoint("a", "00:00:00:00:00:01", 0, 0);
wifi_service->AddEndpoint(endpoint);
EXPECT_TRUE(wifi_service->IsVisible());
Mock::VerifyAndClearExpectations(adaptor);
// Removing the last endpoint emits a change: Visible = false.
EXPECT_CALL(*adaptor, EmitBoolChanged(kVisibleProperty, false));
wifi_service->RemoveEndpoint(endpoint);
EXPECT_FALSE(wifi_service->IsVisible());
Mock::VerifyAndClearExpectations(adaptor);
// Entering the a connecting state emits a change: Visible = true
// although the service has no endpoints.
EXPECT_CALL(*adaptor, EmitBoolChanged(kVisibleProperty, true));
wifi_service->SetState(Service::kStateAssociating);
EXPECT_TRUE(wifi_service->IsVisible());
Mock::VerifyAndClearExpectations(adaptor);
// Moving between connecting / connected states does not trigger an Emit.
EXPECT_CALL(*adaptor, EmitBoolChanged(kVisibleProperty, _)).Times(0);
wifi_service->SetState(Service::kStateConfiguring);
EXPECT_TRUE(wifi_service->IsVisible());
Mock::VerifyAndClearExpectations(adaptor);
// Entering the Idle state emits a change: Visible = false.
EXPECT_CALL(*adaptor, EmitBoolChanged(kVisibleProperty, false));
wifi_service->SetState(Service::kStateIdle);
EXPECT_FALSE(wifi_service->IsVisible());
Mock::VerifyAndClearExpectations(adaptor);
}
} // namespace shill