// // Copyright (C) 2012 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include "shill/wifi/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> #if defined(__ANDROID__) #include <dbus/service_constants.h> #else #include <chromeos/dbus/service_constants.h> #endif // __ANDROID__ #include <gmock/gmock.h> #include <gtest/gtest.h> #include "shill/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/property_store_unittest.h" #include "shill/refptr_types.h" #include "shill/service_property_change_test.h" #include "shill/supplicant/wpa_supplicant.h" #include "shill/technology.h" #include "shill/tethering.h" #include "shill/wifi/mock_wifi.h" #include "shill/wifi/mock_wifi_provider.h" #include "shill/wifi/wifi_endpoint.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()), 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(Service::kReasonCredentialsLoaded); return service->connectable(); } WiFiEndpoint* MakeEndpoint(const string& ssid, const string& bssid, uint16_t frequency, int16_t signal_dbm, bool has_wpa_property, bool has_rsn_property) { return WiFiEndpoint::MakeEndpoint( nullptr, wifi(), ssid, bssid, WPASupplicant::kNetworkModeInfrastructure, frequency, signal_dbm, has_wpa_property, has_rsn_property); } WiFiEndpoint* MakeOpenEndpoint(const string& ssid, const string& bssid, uint16_t frequency, int16_t signal_dbm) { return WiFiEndpoint::MakeOpenEndpoint( nullptr, wifi(), ssid, bssid, WPASupplicant::kNetworkModeInfrastructure, frequency, signal_dbm); } WiFiEndpoint* MakeOpenEndpointWithWiFi(WiFiRefPtr wifi, const string& ssid, const string& bssid, uint16_t frequency, int16_t signal_dbm) { return WiFiEndpoint::MakeOpenEndpoint( nullptr, 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, kSecurityNone, false); } scoped_refptr<MockWiFi> MakeSimpleWiFi(const string& link_name) { return new NiceMock<MockWiFi>(control_interface(), dispatcher(), metrics(), manager(), link_name, fake_mac, 0); } ServiceMockAdaptor* GetAdaptor(WiFiService* service) { return static_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(); } bool SetRoamThreshold(WiFiServiceRefPtr service, uint16_t threshold) { return service->SetRoamThreshold(threshold, nullptr); } uint16_t GetRoamThreshold(WiFiServiceRefPtr service) const { return service->GetRoamThreshold(nullptr); } 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(base::ToLowerASCII(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.get())) { 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_t kOkEndpointFrequency = 2422; static const uint16_t kBadEndpointFrequency = 2417; static const uint16_t kGoodEndpointFrequency = 2412; static const int16_t kOkEndpointSignal = -50; static const int16_t kBadEndpointSignal = -75; static const int16_t 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_t kOkEndpointStrength; const uint8_t kBadEndpointStrength; const uint8_t 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(base::ToLowerASCII(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); brillo::VariantDictionary properties; // if service doesn't propertly sanitize SSID, this will generate SIGABRT. EXPECT_TRUE(wifi_service->store().GetProperties(&properties, nullptr)); } MATCHER(PSKSecurityArgs, "") { return arg.ContainsString(WPASupplicant::kPropertySecurityProtocol) && arg.GetString(WPASupplicant::kPropertySecurityProtocol) == string("WPA RSN") && arg.ContainsString(WPASupplicant::kPropertyPreSharedKey); } MATCHER_P(FrequencyArg, has_arg, "") { return has_arg == arg.ContainsInt(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(nullptr, "in test"); } TEST_F(WiFiServiceTest, ConnectWithPreferredDevice) { // Setup service, device, and endpoints. WiFiServiceRefPtr wifi_service = MakeServiceWithMockManager(); const string kDeviceName1 = "test_device1"; const string kDeviceName2 = "test_device2"; scoped_refptr<MockWiFi> wifi1 = MakeSimpleWiFi(kDeviceName1); scoped_refptr<MockWiFi> wifi2 = MakeSimpleWiFi(kDeviceName2); WiFiEndpointRefPtr endpoint1 = MakeOpenEndpointWithWiFi(wifi1, "a", "00:00:00:00:00:01", 0, 0); WiFiEndpointRefPtr endpoint2 = MakeOpenEndpointWithWiFi(wifi2, "a", "00:00:00:00:00:01", 0, 0); wifi_service->SetPreferredDevice(kDeviceName1, nullptr); wifi_service->AddEndpoint(endpoint1); wifi_service->AddEndpoint(endpoint2); EXPECT_EQ(wifi1, wifi_service->wifi_); EXPECT_CALL(*wifi1, ConnectTo(wifi_service.get())); EXPECT_CALL(*wifi2, ConnectTo(_)).Times(0); wifi_service->Connect(nullptr, "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(nullptr, "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(nullptr, "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().get()); // 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().get()); } 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(nullptr, "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(Service::kReasonCredentialsLoaded); EXPECT_CALL(*wifi(), ConnectTo(service.get())); service->Connect(nullptr, "in test"); KeyValueStore params = service->GetSupplicantConfigurationParameters(); EXPECT_TRUE( params.ContainsString(WPASupplicant::kNetworkPropertyEapIdentity)); EXPECT_TRUE(params.ContainsString(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::kReasonCredentialsLoaded); service->Connect(nullptr, "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(nullptr, "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(nullptr, "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(nullptr, "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(nullptr, "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(nullptr, "in test"); KeyValueStore params = wifi_service->GetSupplicantConfigurationParameters(); EXPECT_TRUE(params.ContainsString(WPASupplicant::kPropertySecurityProtocol)); EXPECT_TRUE(params.ContainsString(WPASupplicant::kPropertyPreSharedKey)); EXPECT_TRUE(params.ContainsUint(WPASupplicant::kNetworkPropertyIeee80211w)); } MATCHER_P(WEPSecurityArgsKeyIndex, index, "") { uint32_t index_u32 = index; return arg.ContainsString(WPASupplicant::kPropertyAuthAlg) && arg.ContainsUint8s( WPASupplicant::kPropertyWEPKey + base::IntToString(index)) && arg.ContainsUint(WPASupplicant::kPropertyWEPTxKeyIndex) && (arg.GetUint(WPASupplicant::kPropertyWEPTxKeyIndex) == 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(nullptr, "in test"); EXPECT_THAT(wifi_service->GetSupplicantConfigurationParameters(), WEPSecurityArgsKeyIndex(0)); wifi_service->SetPassphrase("abcdefghijklm", &error); EXPECT_CALL(*wifi(), ConnectTo(wifi_service.get())); wifi_service->Connect(nullptr, "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(nullptr, "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(nullptr, "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(nullptr, "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", nullptr); wifi_service->mutable_eap()->set_identity("something"); wifi_service->mutable_eap()->set_password("mumble"); wifi_service->OnEapCredentialsChanged(Service::kReasonCredentialsLoaded); EXPECT_CALL(*wifi(), ConnectTo(wifi_service.get())); wifi_service->Connect(nullptr, "in test"); KeyValueStore params = wifi_service->GetSupplicantConfigurationParameters(); EXPECT_TRUE( params.ContainsString(WPASupplicant::kNetworkPropertyEapIdentity)); EXPECT_TRUE(params.ContainsString(WPASupplicant::kNetworkPropertyCaPath)); EXPECT_FALSE( params.ContainsString(WPASupplicant::kPropertySecurityProtocol)); } TEST_F(WiFiServiceTest, SetPassphraseResetHasEverConnected) { WiFiServiceRefPtr wifi_service = MakeServiceWithWiFi(kSecurityRsn); const string kPassphrase = "abcdefgh"; Error error; // A changed passphrase should reset has_ever_connected_ field. wifi_service->has_ever_connected_ = true; EXPECT_TRUE(wifi_service->has_ever_connected()); wifi_service->SetPassphrase(kPassphrase, &error); EXPECT_FALSE(wifi_service->has_ever_connected()); } 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().get()); 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().get()); 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().get()); 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().get()); 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().get()); EXPECT_FALSE(error.IsSuccess()); } { // A change to EAP parameters in a PSK (non 802.1x) service will not // trigger cache removal. wifi_service->has_ever_connected_ = true; EXPECT_TRUE(wifi_service->has_ever_connected()); EXPECT_CALL(*wifi(), ClearCachedCredentials(wifi_service.get())).Times(0); wifi_service->OnEapCredentialsChanged(Service::kReasonPropertyUpdate); EXPECT_TRUE(wifi_service->has_ever_connected()); Mock::VerifyAndClearExpectations(wifi().get()); } WiFiServiceRefPtr eap_wifi_service = MakeServiceWithWiFi(kSecurity8021x); { // Any change to EAP parameters (including a null one) will trigger cache // removal in an 802.1x service. This is a lot less granular than the // passphrase checks above. // Changes in EAP parameters should also clear has_ever_connected_. eap_wifi_service->has_ever_connected_ = true; EXPECT_TRUE(eap_wifi_service->has_ever_connected()); EXPECT_CALL(*wifi(), ClearCachedCredentials(eap_wifi_service.get())); eap_wifi_service->OnEapCredentialsChanged(Service::kReasonPropertyUpdate); EXPECT_FALSE(eap_wifi_service->has_ever_connected()); Mock::VerifyAndClearExpectations(wifi().get()); } } // 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().get()); 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().get()); 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().get()); 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().get()); 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().get()); 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().get()); 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, SetPassphraseForNonPassphraseService) { 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_TRUE(service->Load(&mock_store)); Error error; EXPECT_FALSE(service->SetPassphrase("password", &error)); EXPECT_TRUE(error.type() == Error::kNotSupported); } 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 kStorageId = service->GetStorageIdentifier(); EXPECT_CALL(mock_store, ContainsGroup(StrEq(kStorageId))) .WillRepeatedly(Return(true)); set<string> groups; groups.insert(kStorageId); EXPECT_CALL(mock_store, GetGroupsWithProperties( ContainsWiFiProperties( simple_ssid(), kModeManaged, kSecurityPsk))) .WillRepeatedly(Return(groups)); EXPECT_CALL(mock_store, GetBool(_, _, _)) .WillRepeatedly(Return(false)); const string kPassphrase = "passphrase"; EXPECT_CALL(mock_store, GetCryptedString(StrEq(kStorageId), WiFiService::kStoragePassphrase, _)) .WillRepeatedly(DoAll(SetArgumentPointee<2>(kPassphrase), Return(true))); EXPECT_CALL(mock_store, GetCryptedString(StrEq(kStorageId), StrNe(WiFiService::kStoragePassphrase), _)) .WillRepeatedly(Return(false)); EXPECT_TRUE(service->need_passphrase_); EXPECT_TRUE(service->Load(&mock_store)); EXPECT_EQ(kPassphrase, 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, LoadPassphraseClearCredentials) { const string kOldPassphrase = "oldpassphrase"; const string kPassphrase = "passphrase"; const bool kHasEverConnected = true; WiFiServiceRefPtr service = MakeServiceWithWiFi(kSecurityPsk); NiceMock<MockStore> mock_store; const string kStorageId = service->GetStorageIdentifier(); EXPECT_CALL(mock_store, ContainsGroup(StrEq(kStorageId))) .WillRepeatedly(Return(true)); set<string> groups; groups.insert(kStorageId); EXPECT_CALL(mock_store, GetGroupsWithProperties( ContainsWiFiProperties( simple_ssid(), kModeManaged, kSecurityPsk))) .WillRepeatedly(Return(groups)); EXPECT_CALL(mock_store, GetBool(_, _, _)) .WillRepeatedly(Return(false)); EXPECT_CALL(mock_store, GetCryptedString(StrEq(kStorageId), WiFiService::kStoragePassphrase, _)) .WillRepeatedly(DoAll(SetArgumentPointee<2>(kPassphrase), Return(true))); EXPECT_CALL(mock_store, GetCryptedString(StrEq(kStorageId), StrNe(WiFiService::kStoragePassphrase), _)) .WillRepeatedly(Return(false)); EXPECT_CALL(mock_store, GetBool(kStorageId, Service::kStorageHasEverConnected, _)) .WillRepeatedly(DoAll(SetArgumentPointee<2>(kHasEverConnected), Return(true))); // Set old passphrase for service EXPECT_TRUE(service->need_passphrase_); service->passphrase_ = kOldPassphrase; service->has_ever_connected_ = true; scoped_refptr<MockProfile> mock_profile( new NiceMock<MockProfile>(control_interface(), metrics(), manager())); service->set_profile(mock_profile); // Detect if the service is going to attempt to update the stored profile. EXPECT_CALL(*mock_profile, GetConstStorage()).Times(0); // The kOldPassphrase is different than the newly loaded passhprase, // so the credentials should be cleared. EXPECT_CALL(*wifi(), ClearCachedCredentials(_)).Times(1); EXPECT_CALL(*mock_profile, UpdateService(_)).Times(0); EXPECT_TRUE(service->Load(&mock_store)); EXPECT_EQ(kPassphrase, service->passphrase_); EXPECT_TRUE(service->has_ever_connected_); Mock::VerifyAndClearExpectations(wifi().get()); Mock::VerifyAndClearExpectations(mock_profile.get()); // Repeat Service::Load with same old and new passphrase. Since the old // and new passphrase match, verify the cache is not cleared during // profile load. service->set_profile(mock_profile); EXPECT_CALL(*mock_profile, GetConstStorage()).Times(0); EXPECT_CALL(*wifi(), ClearCachedCredentials(_)).Times(0); EXPECT_TRUE(service->Load(&mock_store)); EXPECT_EQ(kPassphrase, service->passphrase_); EXPECT_TRUE(service->has_ever_connected_); } 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, nullptr); 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, nullptr)); 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, nullptr)); 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(), DisconnectFromIfActive(service.get())).Times(1); Error error; service->Disconnect(&error, "in test"); } TEST_F(WiFiServiceTest, DisconnectWithoutWiFi) { WiFiServiceRefPtr service = MakeSimpleService(kSecurityWep); EXPECT_CALL(*wifi(), DisconnectFrom(_)).Times(0); Error error; service->Disconnect(&error, "in test"); 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, "in test"); 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(), DisconnectFromIfActive(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(), DisconnectFromIfActive(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(base::ToLowerASCII(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(base::ToLowerASCII(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, nullptr, 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, nullptr, 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, nullptr, false)); EXPECT_FALSE(CheckConnectable(kSecurityWpa, "a", false)); // 802.1x without connectable EAP credentials should NOT be connectable. EXPECT_FALSE(CheckConnectable(kSecurity8021x, nullptr, false)); // 802.1x with connectable EAP credentials should be connectable. EXPECT_TRUE(CheckConnectable(kSecurity8021x, nullptr, true)); // Dynamic WEP + 802.1X should be connectable under the same conditions. EXPECT_TRUE(CheckConnectable(kSecurityWep, nullptr, 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_); Error error; const string kPassphrase = "0:abcde"; EXPECT_TRUE( wifi_service->mutable_store()->SetAnyProperty(kPassphraseProperty, brillo::Any(kPassphrase), &error)); EXPECT_EQ(kPassphrase, wifi_service->passphrase_); EXPECT_TRUE(wifi_service->mutable_store()->ClearProperty(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_t i = std::numeric_limits<int16_t>::min(); i < 0; ++i) { int16_t current_mapped = WiFiService::SignalToStrength(i); int16_t 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_t i = 1; i < std::numeric_limits<int16_t>::max(); ++i) { int16_t current_mapped = WiFiService::SignalToStrength(i); int16_t 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(nullptr); 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); } TEST_F(WiFiServiceUpdateFromEndpointsTest, AddEndpointWithPreferredDevice) { // Setup service, device, and endpoints. WiFiServiceRefPtr wifi_service = MakeServiceWithMockManager(); const string kDeviceName1 = "test_device1"; const string kDeviceName2 = "test_device2"; scoped_refptr<MockWiFi> wifi1 = MakeSimpleWiFi(kDeviceName1); scoped_refptr<MockWiFi> wifi2 = MakeSimpleWiFi(kDeviceName2); // Best signal for endpoint associated with the preferred device. const int16_t kPreferredDeviceBestSignal = -40; WiFiEndpointRefPtr endpoint0 = MakeOpenEndpointWithWiFi(wifi2, "a", "00:00:00:00:00:01", 0, kPreferredDeviceBestSignal + 10); WiFiEndpointRefPtr endpoint1 = MakeOpenEndpointWithWiFi(wifi1, "a", "00:00:00:00:00:01", 0, kPreferredDeviceBestSignal - 10); WiFiEndpointRefPtr endpoint2 = MakeOpenEndpointWithWiFi(wifi1, "a", "00:00:00:00:00:01", 0, kPreferredDeviceBestSignal); WiFiEndpointRefPtr endpoint3 = MakeOpenEndpointWithWiFi(wifi2, "a", "00:00:00:00:00:01", 0, kPreferredDeviceBestSignal + 10); wifi_service->SetPreferredDevice(kDeviceName1, nullptr); wifi_service->AddEndpoint(endpoint0); wifi_service->AddEndpoint(endpoint1); wifi_service->AddEndpoint(endpoint2); wifi_service->AddEndpoint(endpoint3); EXPECT_EQ(wifi1, wifi_service->wifi_); // Service should display the signal strength of the best signal endpoint // that's associated with the preferred device. EXPECT_EQ(WiFiService::SignalToStrength(kPreferredDeviceBestSignal), wifi_service->strength()); } MATCHER_P(IsSetwiseEqual, expected_set, "") { set<uint16_t> 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_t>(), service->frequency_list()); // Add endpoint -> endpoint's frequency in list. EXPECT_CALL(adaptor, EmitUint16sChanged( kWifiFrequencyListProperty, vector<uint16_t>{kGoodEndpointFrequency})); service->AddEndpoint(good_endpoint); Mock::VerifyAndClearExpectations(&adaptor); // Add another endpoint -> both frequencies in list. // Order doesn't matter. set<uint16_t> 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_t>{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_t>{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_t>{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_t>{})); service->RemoveEndpoint(same_freq_as_ok_endpoint); Mock::VerifyAndClearExpectations(&adaptor); } TEST_F(WiFiServiceTest, SecurityFromCurrentEndpoint) { WiFiServiceRefPtr service(MakeSimpleService(kSecurityPsk)); EXPECT_EQ(kSecurityPsk, service->GetSecurity(nullptr)); WiFiEndpoint* endpoint = MakeOpenEndpoint( simple_ssid_string(), "00:00:00:00:00:00", 0, 0); service->AddEndpoint(endpoint); EXPECT_EQ(kSecurityPsk, service->GetSecurity(nullptr)); service->NotifyCurrentEndpoint(endpoint); EXPECT_EQ(kSecurityNone, service->GetSecurity(nullptr)); service->NotifyCurrentEndpoint(nullptr); EXPECT_EQ(kSecurityPsk, service->GetSecurity(nullptr)); } 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.get()); 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 resets because of a credential change. // A credential change changes the has_ever_connected to false and // immediately returns true when attempting to add the failure. Error error; service->SetPassphrase("Panchromatic Resonance", &error); EXPECT_TRUE(error.IsSuccess()); EXPECT_TRUE(service->AddSuspectedCredentialFailure()); EXPECT_EQ(0, service->suspected_credential_failures_); // Make sure that we still return true after resetting the failure // count. service->suspected_credential_failures_ = 3; EXPECT_EQ(3, service->suspected_credential_failures_); service->ResetSuspectedCredentialFailures(); EXPECT_EQ(0, service->suspected_credential_failures_); EXPECT_TRUE(service->AddSuspectedCredentialFailure()); } TEST_F(WiFiServiceTest, GetTethering) { WiFiServiceRefPtr service = MakeSimpleService(kSecurityNone); EXPECT_EQ(kTetheringNotDetectedState, service->GetTethering(nullptr)); // 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(nullptr)); Mock::VerifyAndClearExpectations(wifi().get()); 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(nullptr)); EXPECT_EQ(kTetheringNotDetectedState, service->GetTethering(nullptr)); Mock::VerifyAndClearExpectations(wifi().get()); // 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(nullptr)); // 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(nullptr)); // Continue in the un-tethered device case for a few more tests below. Mock::VerifyAndClearExpectations(wifi().get()); 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(nullptr)); // 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(nullptr)); // 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(nullptr)); // If the device reports that it is tethered, this should override any // findings gained from examining the endpoints. Mock::VerifyAndClearExpectations(wifi().get()); EXPECT_CALL(*wifi(), IsConnectedViaTether()).WillOnce(Return(true)); EXPECT_EQ(kTetheringConfirmedState, service->GetTethering(nullptr)); } TEST_F(WiFiServiceTest, IsVisible) { WiFiServiceRefPtr wifi_service = MakeServiceWithWiFi(kSecurityNone); ServiceMockAdaptor* adaptor = GetAdaptor(wifi_service.get()); // 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); } TEST_F(WiFiServiceTest, ConfigurePreferredDevice) { const string kDeviceName = "test_device"; WiFiServiceRefPtr service = MakeGenericService(); KeyValueStore args; args.SetString(kWifiPreferredDeviceProperty, kDeviceName); // With no wifi device. Error error; service->Configure(args, &error); EXPECT_EQ(Error::kSuccess, error.type()); EXPECT_EQ(kDeviceName, service->preferred_device_); // With non-preferred wifi device. SetWiFiForService(service, wifi()); service->Configure(args, &error); EXPECT_EQ(Error::kSuccess, error.type()); EXPECT_EQ(nullptr, service->wifi_); EXPECT_EQ(kDeviceName, service->preferred_device_); // With preferred wifi device. scoped_refptr<MockWiFi> preferred_wifi = MakeSimpleWiFi(kDeviceName); SetWiFiForService(service, preferred_wifi); service->Configure(args, &error); EXPECT_EQ(Error::kSuccess, error.type()); EXPECT_EQ(preferred_wifi, service->wifi_); EXPECT_EQ(kDeviceName, service->preferred_device_); } TEST_F(WiFiServiceTest, LoadAndUnloadPreferredDevice) { WiFiServiceRefPtr service = MakeGenericService(); NiceMock<MockStore> mock_store; const string kStorageId = service->GetStorageIdentifier(); EXPECT_CALL(mock_store, ContainsGroup(StrEq(kStorageId))) .WillRepeatedly(Return(true)); set<string> groups; groups.insert(kStorageId); EXPECT_CALL(mock_store, GetGroupsWithProperties( ContainsWiFiProperties( simple_ssid(), kModeManaged, kSecurityWep))) .WillRepeatedly(Return(groups)); EXPECT_CALL(mock_store, GetBool(_, _, _)) .WillRepeatedly(Return(false)); const string kDeviceName = "test_device"; EXPECT_CALL(mock_store, GetString(StrEq(kStorageId), WiFiService::kStoragePreferredDevice, _)) .WillRepeatedly(DoAll(SetArgumentPointee<2>(kDeviceName), Return(true))); EXPECT_CALL(mock_store, GetString(StrEq(kStorageId), StrNe(WiFiService::kStoragePreferredDevice), _)) .WillRepeatedly(Return(false)); // With no wifi device. EXPECT_TRUE(service->Load(&mock_store)); EXPECT_EQ(kDeviceName, service->preferred_device_); service->Unload(); EXPECT_EQ("", service->preferred_device_); // With non-preferred wifi device. SetWiFiForService(service, wifi()); EXPECT_TRUE(service->Load(&mock_store)); EXPECT_EQ(nullptr, service->wifi_); EXPECT_EQ(kDeviceName, service->preferred_device_); service->Unload(); EXPECT_EQ("", service->preferred_device_); // With preferred wifi device. scoped_refptr<MockWiFi> preferred_wifi = MakeSimpleWiFi(kDeviceName); SetWiFiForService(service, preferred_wifi); EXPECT_TRUE(service->Load(&mock_store)); EXPECT_EQ(preferred_wifi, service->wifi_); EXPECT_EQ(kDeviceName, service->preferred_device_); service->Unload(); EXPECT_EQ("", service->preferred_device_); } TEST_F(WiFiServiceTest, ChooseDevice) { const string kDeviceName1 = "test_device1"; const string kDeviceName2 = "test_device2"; scoped_refptr<MockWiFi> wifi1 = MakeSimpleWiFi(kDeviceName1); scoped_refptr<MockWiFi> wifi2 = MakeSimpleWiFi(kDeviceName2); WiFiServiceRefPtr service = MakeServiceWithMockManager(); // No preferred device. EXPECT_CALL(*mock_manager(), GetEnabledDeviceByLinkName(_)).Times(0); EXPECT_CALL(*mock_manager(), GetEnabledDeviceWithTechnology(Technology::kWifi)) .WillOnce(Return(wifi1)); EXPECT_EQ(wifi1, service->ChooseDevice()); Mock::VerifyAndClearExpectations(mock_manager()); // With preferred device. service->SetPreferredDevice(kDeviceName2, nullptr); EXPECT_CALL(*mock_manager(), GetEnabledDeviceByLinkName(kDeviceName2)) .WillOnce(Return(wifi2)); EXPECT_CALL(*mock_manager(), GetEnabledDeviceWithTechnology(_)).Times(0); EXPECT_EQ(wifi2, service->ChooseDevice()); Mock::VerifyAndClearExpectations(mock_manager()); } TEST_F(WiFiServiceTest, RoamThresholdProperty) { WiFiServiceRefPtr service = MakeGenericService(); static const uint16_t kRoamThreshold16 = 16; static const uint16_t kRoamThreshold32 = 32; EXPECT_TRUE(SetRoamThreshold(service, kRoamThreshold16)); EXPECT_EQ(GetRoamThreshold(service), kRoamThreshold16); // Try a different number EXPECT_TRUE(SetRoamThreshold(service, kRoamThreshold32)); EXPECT_EQ(GetRoamThreshold(service), kRoamThreshold32); } TEST_F(WiFiServiceTest, SaveLoadRoamThreshold) { WiFiServiceRefPtr service = MakeGenericService(); NiceMock<MockStore> mock_store; const uint16_t kRoamThreshold = 10; const string kStorageId = service->GetStorageIdentifier(); EXPECT_CALL(mock_store, ContainsGroup(StrEq(kStorageId))) .WillRepeatedly(Return(true)); set<string> groups; groups.insert(kStorageId); EXPECT_CALL(mock_store, GetGroupsWithProperties(ContainsWiFiProperties( simple_ssid(), kModeManaged, kSecurityWep))) .WillRepeatedly(Return(groups)); EXPECT_CALL(mock_store, GetBool(_, _, _)).Times(AnyNumber()); EXPECT_CALL(mock_store, SetBool(_, _, _)).Times(AnyNumber()); // First, save these values. service->roam_threshold_db_ = kRoamThreshold; service->roam_threshold_db_set_ = true; EXPECT_CALL(mock_store, SetUint64(StrEq(kStorageId), WiFiService::kStorageRoamThreshold, kRoamThreshold)); EXPECT_CALL(mock_store, SetBool(StrEq(kStorageId), WiFiService::kStorageRoamThresholdSet, true)); EXPECT_TRUE(service->Save(&mock_store)); // Then, load these values into the WiFiService members. service->roam_threshold_db_ = 0; service->roam_threshold_db_set_ = false; EXPECT_CALL(mock_store, GetUint64(StrEq(kStorageId), WiFiService::kStorageRoamThreshold, _)) .WillOnce(DoAll(SetArgumentPointee<2>(kRoamThreshold), Return(true))); EXPECT_CALL(mock_store, GetBool(StrEq(kStorageId), WiFiService::kStorageRoamThresholdSet, _)) .WillOnce(DoAll(SetArgumentPointee<2>(true), Return(true))); EXPECT_TRUE(service->Load(&mock_store)); EXPECT_EQ(kRoamThreshold, service->roam_threshold_db_); EXPECT_TRUE(service->roam_threshold_db_set_); } } // namespace shill