| // Copyright 2018 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "shill/service.h" |
| |
| #include <cstdint> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include <base/containers/contains.h> |
| #include <base/functional/bind.h> |
| #include <base/memory/scoped_refptr.h> |
| #include <base/task/single_thread_task_runner.h> |
| #include <base/test/bind.h> |
| #include <base/test/test_future.h> |
| #include <base/time/time.h> |
| #include <chromeos/dbus/service_constants.h> |
| #include <chromeos/patchpanel/dbus/fake_client.h> |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| #include <metrics/metrics_library_mock.h> |
| #include <net-base/http_url.h> |
| #include <net-base/mac_address.h> |
| |
| #include "shill/dbus/dbus_control.h" |
| #include "shill/error.h" |
| #include "shill/ethernet/ethernet_service.h" |
| #include "shill/http_request.h" |
| #include "shill/ipconfig.h" |
| #include "shill/manager.h" |
| #include "shill/metrics.h" |
| #include "shill/mock_adaptors.h" |
| #include "shill/mock_device.h" |
| #include "shill/mock_eap_credentials.h" |
| #include "shill/mock_event_dispatcher.h" |
| #include "shill/mock_log.h" |
| #include "shill/mock_manager.h" |
| #include "shill/mock_power_manager.h" |
| #include "shill/mock_profile.h" |
| #include "shill/mock_service.h" |
| #include "shill/mock_time.h" |
| #include "shill/network/mock_network.h" |
| #include "shill/network/network_monitor.h" |
| #include "shill/network/portal_detector.h" |
| #include "shill/service_property_change_test.h" |
| #include "shill/service_under_test.h" |
| #include "shill/store/fake_store.h" |
| #include "shill/store/property_store_test.h" |
| #include "shill/testing.h" |
| |
| using net_base::IPAddress; |
| using net_base::IPCIDR; |
| using net_base::IPv4Address; |
| using net_base::IPv4CIDR; |
| using net_base::IPv6Address; |
| using net_base::IPv6CIDR; |
| using net_base::NetworkConfig; |
| |
| using testing::_; |
| using testing::AnyNumber; |
| using testing::AtLeast; |
| using testing::DefaultValue; |
| using testing::DoAll; |
| using testing::Eq; |
| using testing::Ge; |
| using testing::HasSubstr; |
| using testing::Mock; |
| using testing::NiceMock; |
| using testing::Pointer; |
| using testing::Return; |
| using testing::ReturnNull; |
| using testing::ReturnRef; |
| using testing::SetArgPointee; |
| using testing::StrictMock; |
| using testing::Test; |
| using testing::Values; |
| |
| namespace { |
| const char kConnectDisconnectReason[] = "RPC"; |
| const char kGUID[] = "guid"; |
| const char kIfName[] = "testdevice"; |
| } // namespace |
| |
| namespace shill { |
| |
| class ServiceTest : public PropertyStoreTest { |
| public: |
| ServiceTest() |
| : mock_manager_(control_interface(), dispatcher(), metrics()), |
| service_(new ServiceUnderTest(&mock_manager_)), |
| service2_(new ServiceUnderTest(&mock_manager_)), |
| storage_id_(ServiceUnderTest::kStorageId), |
| power_manager_(new MockPowerManager(control_interface())) { |
| ON_CALL(*control_interface(), CreatePowerManagerProxy(_, _, _)) |
| .WillByDefault(ReturnNull()); |
| |
| service_->disconnects_.time_ = &time_; |
| service_->misconnects_.time_ = &time_; |
| DefaultValue<Timestamp>::Set(Timestamp()); |
| service_->eap_ = std::make_unique<NiceMock<MockEapCredentials>>(); |
| mock_manager_.running_ = true; |
| mock_manager_.set_power_manager(power_manager_); // Passes ownership. |
| } |
| |
| ~ServiceTest() override = default; |
| |
| MOCK_METHOD(void, TestCallback, (const Error&)); |
| |
| protected: |
| using MockProfileRefPtr = scoped_refptr<MockProfile>; |
| |
| ServiceMockAdaptor* GetAdaptor() { |
| return static_cast<ServiceMockAdaptor*>(service_->adaptor()); |
| } |
| |
| std::string GetFriendlyName() { return service_->friendly_name(); } |
| |
| void SetManagerRunning(bool running) { mock_manager_.running_ = running; } |
| |
| void SetSuspending(bool suspending) { |
| power_manager_->suspending_ = suspending; |
| } |
| |
| bool GetExplicitlyDisconnected() const { |
| return service_->explicitly_disconnected_; |
| } |
| |
| void SetExplicitlyDisconnected(bool explicitly) { |
| service_->explicitly_disconnected_ = explicitly; |
| } |
| |
| void SetStateField(Service::ConnectState state) { service_->state_ = state; } |
| |
| Service::ConnectState GetPreviousState() const { |
| return service_->previous_state_; |
| } |
| |
| void SetTechnology(Technology technology) { |
| service_->technology_ = technology; |
| } |
| |
| void NoteFailureEvent() { service_->NoteFailureEvent(); } |
| |
| EventHistory* GetDisconnects() { return &service_->disconnects_; } |
| EventHistory* GetMisconnects() { return &service_->misconnects_; } |
| |
| Timestamp GetTimestamp(int monotonic_seconds, |
| int boottime_seconds, |
| const std::string& wall_clock) { |
| struct timeval monotonic = {.tv_sec = monotonic_seconds, .tv_usec = 0}; |
| struct timeval boottime = {.tv_sec = boottime_seconds, .tv_usec = 0}; |
| return Timestamp(monotonic, boottime, wall_clock); |
| } |
| |
| void PushTimestamp(EventHistory* events, |
| int monotonic_seconds, |
| int boottime_seconds, |
| const std::string& wall_clock) { |
| events->RecordEventInternal( |
| GetTimestamp(monotonic_seconds, boottime_seconds, wall_clock)); |
| } |
| |
| int GetDisconnectsMonitorSeconds() { |
| return Service::kDisconnectsMonitorSeconds; |
| } |
| |
| int GetMisconnectsMonitorSeconds() { |
| return Service::kMisconnectsMonitorSeconds; |
| } |
| |
| int GetMaxDisconnectEventHistory() { |
| return Service::kMaxDisconnectEventHistory; |
| } |
| |
| int GetMaxMisconnectEventHistory() { |
| return Service::kMaxMisconnectEventHistory; |
| } |
| |
| bool GetAutoConnect(Error* error) { return service_->GetAutoConnect(error); } |
| |
| void ClearAutoConnect(Error* error) { service_->ClearAutoConnect(error); } |
| |
| bool IsAutoConnectable(const char** reason) { |
| return service_->IsAutoConnectable(reason); |
| } |
| |
| bool SetAutoConnectFull(bool connect, Error* error) { |
| return service_->SetAutoConnectFull(connect, error); |
| } |
| |
| const base::CancelableOnceClosure& GetPendingConnectTask() { |
| return service_->pending_connect_task_; |
| } |
| |
| bool HasPendingConnect() { return !GetPendingConnectTask().IsCancelled(); } |
| |
| bool SortingOrderIs(const ServiceRefPtr& service0, |
| const ServiceRefPtr& service1, |
| bool should_compare_connectivity_state) { |
| return Service::Compare(service0, service1, |
| should_compare_connectivity_state, |
| technology_order_for_sorting_) |
| .first; |
| } |
| |
| bool DefaultSortingOrderIs(const ServiceRefPtr& service0, |
| const ServiceRefPtr& service1) { |
| const bool kShouldCompareConnectivityState = true; |
| return SortingOrderIs(service0, service1, kShouldCompareConnectivityState); |
| } |
| |
| std::optional<base::TimeDelta> GetTimeSinceFailed() { |
| // Wait 1 MS before calling GetTimeSinceFailed. |
| base::test::TestFuture<void> future; |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask( |
| FROM_HERE, future.GetCallback(), base::Milliseconds(1)); |
| EXPECT_TRUE(future.Wait()); |
| return service_->GetTimeSinceFailed(); |
| } |
| |
| patchpanel::Client::TrafficCounter CreateCounter( |
| patchpanel::Client::TrafficVector counters, |
| patchpanel::Client::TrafficSource source, |
| const std::string& device_name) { |
| patchpanel::Client::TrafficCounter counter; |
| counter.traffic = counters; |
| counter.source = source; |
| counter.ifname = device_name; |
| return counter; |
| } |
| |
| bool SetAnyPropertyAndEnsureSuccess(const std::string& name, |
| const brillo::Any& value) { |
| Error error; |
| service_->mutable_store()->SetAnyProperty(name, value, &error); |
| return error.IsSuccess(); |
| } |
| |
| NiceMock<MockManager> mock_manager_; |
| NiceMock<MockTime> time_; |
| scoped_refptr<ServiceUnderTest> service_; |
| scoped_refptr<ServiceUnderTest> service2_; |
| std::string storage_id_; |
| MockPowerManager* power_manager_; // Owned by |mock_manager_|. |
| std::vector<Technology> technology_order_for_sorting_; |
| |
| void SetUplinkSpeedKbps(uint32_t uplink_speed_kbps) { |
| service_->SetUplinkSpeedKbps(uplink_speed_kbps); |
| } |
| |
| void SetDownlinkSpeedKbps(uint32_t downlink_speed_kbps) { |
| service_->SetDownlinkSpeedKbps(downlink_speed_kbps); |
| } |
| |
| uint32_t uplink_speed_kbps() { return service_->uplink_speed_kbps(); } |
| |
| uint32_t downlink_speed_kbps() { return service_->downlink_speed_kbps(); } |
| }; |
| |
| class AllMockServiceTest : public testing::Test { |
| public: |
| AllMockServiceTest() |
| : manager_(&control_interface_, &dispatcher_, &metrics_), |
| service_(new ServiceUnderTest(&manager_)) {} |
| ~AllMockServiceTest() override = default; |
| |
| protected: |
| MockControl control_interface_; |
| StrictMock<MockEventDispatcher> dispatcher_; |
| NiceMock<MockMetrics> metrics_; |
| MockManager manager_; |
| scoped_refptr<ServiceUnderTest> service_; |
| }; |
| |
| TEST_F(ServiceTest, Constructor) { |
| EXPECT_TRUE(service_->save_credentials_); |
| EXPECT_EQ(Service::CheckPortalState::kAutomatic, service_->check_portal()); |
| EXPECT_EQ("auto", service_->GetCheckPortal(nullptr)); |
| EXPECT_EQ(Service::kStateIdle, service_->state()); |
| EXPECT_FALSE(service_->has_ever_connected()); |
| EXPECT_EQ(0, service_->previous_error_serial_number_); |
| EXPECT_EQ("", service_->previous_error_); |
| } |
| |
| TEST_F(ServiceTest, CalculateState) { |
| service_->state_ = Service::kStateConnected; |
| Error error; |
| EXPECT_EQ(kStateReady, service_->CalculateState(&error)); |
| EXPECT_TRUE(error.IsSuccess()); |
| } |
| |
| TEST_F(ServiceTest, CalculateTechnology) { |
| service_->technology_ = Technology::kWiFi; |
| Error error; |
| EXPECT_EQ(kTypeWifi, service_->CalculateTechnology(&error)); |
| EXPECT_TRUE(error.IsSuccess()); |
| } |
| |
| TEST_F(ServiceTest, GetProperties) { |
| { |
| brillo::VariantDictionary props; |
| Error error; |
| std::string expected("true"); |
| service_->mutable_store()->SetStringProperty(kCheckPortalProperty, expected, |
| &error); |
| EXPECT_TRUE(service_->store().GetProperties(&props, &error)); |
| ASSERT_FALSE(props.find(kCheckPortalProperty) == props.end()); |
| EXPECT_TRUE(props[kCheckPortalProperty].IsTypeCompatible<std::string>()); |
| EXPECT_EQ(props[kCheckPortalProperty].Get<std::string>(), expected); |
| } |
| { |
| brillo::VariantDictionary props; |
| Error error; |
| bool expected = true; |
| service_->mutable_store()->SetBoolProperty(kAutoConnectProperty, expected, |
| &error); |
| EXPECT_TRUE(service_->store().GetProperties(&props, &error)); |
| ASSERT_FALSE(props.find(kAutoConnectProperty) == props.end()); |
| EXPECT_TRUE(props[kAutoConnectProperty].IsTypeCompatible<bool>()); |
| EXPECT_EQ(props[kAutoConnectProperty].Get<bool>(), expected); |
| } |
| { |
| brillo::VariantDictionary props; |
| Error error; |
| EXPECT_TRUE(service_->store().GetProperties(&props, &error)); |
| ASSERT_FALSE(props.find(kConnectableProperty) == props.end()); |
| EXPECT_TRUE(props[kConnectableProperty].IsTypeCompatible<bool>()); |
| EXPECT_EQ(props[kConnectableProperty].Get<bool>(), true); |
| } |
| { |
| brillo::VariantDictionary props; |
| Error error; |
| int32_t expected = 127; |
| service_->mutable_store()->SetInt32Property(kPriorityProperty, expected, |
| &error); |
| EXPECT_TRUE(service_->store().GetProperties(&props, &error)); |
| ASSERT_FALSE(props.find(kPriorityProperty) == props.end()); |
| EXPECT_TRUE(props[kPriorityProperty].IsTypeCompatible<int32_t>()); |
| EXPECT_EQ(props[kPriorityProperty].Get<int32_t>(), expected); |
| } |
| { |
| brillo::VariantDictionary props; |
| Error error; |
| service_->store().GetProperties(&props, &error); |
| ASSERT_FALSE(props.find(kDeviceProperty) == props.end()); |
| EXPECT_TRUE(props[kDeviceProperty].IsTypeCompatible<dbus::ObjectPath>()); |
| EXPECT_EQ(props[kDeviceProperty].Get<dbus::ObjectPath>(), |
| ServiceUnderTest::kRpcId); |
| } |
| } |
| |
| TEST_F(ServiceTest, SetProperty) { |
| { |
| EXPECT_TRUE(SetAnyPropertyAndEnsureSuccess(kSaveCredentialsProperty, |
| PropertyStoreTest::kBoolV)); |
| } |
| { |
| const int32_t priority = 1; |
| EXPECT_TRUE(SetAnyPropertyAndEnsureSuccess(kPriorityProperty, |
| brillo::Any(priority))); |
| } |
| { |
| const std::string guid("not default"); |
| EXPECT_TRUE( |
| SetAnyPropertyAndEnsureSuccess(kGuidProperty, brillo::Any(guid))); |
| } |
| // Ensure that EAP properties cannot be set on services with no EAP |
| // credentials. Use service2_ here since we're have some code in |
| // ServiceTest::SetUp() that fiddles with service_->eap_. |
| std::string eap("eap eep eip!"); |
| { |
| Error error; |
| service2_->mutable_store()->SetAnyProperty(kEapMethodProperty, |
| brillo::Any(eap), &error); |
| ASSERT_TRUE(error.IsFailure()); |
| EXPECT_EQ(Error::kInvalidProperty, error.type()); |
| } |
| { |
| // Now plumb in eap credentials, and try again. |
| Error error; |
| service2_->SetEapCredentials(new EapCredentials()); |
| service2_->mutable_store()->SetAnyProperty(kEapMethodProperty, |
| brillo::Any(eap), &error); |
| EXPECT_TRUE(error.IsSuccess()); |
| } |
| // Ensure that an attempt to write a R/O property returns InvalidArgs error. |
| { |
| Error error; |
| service_->mutable_store()->SetAnyProperty( |
| kConnectableProperty, PropertyStoreTest::kBoolV, &error); |
| ASSERT_TRUE(error.IsFailure()); |
| EXPECT_EQ(Error::kInvalidArguments, error.type()); |
| } |
| { |
| bool auto_connect = true; |
| Error error; |
| service_->mutable_store()->SetAnyProperty( |
| kAutoConnectProperty, brillo::Any(auto_connect), &error); |
| EXPECT_TRUE(error.IsSuccess()); |
| } |
| // Ensure that we can perform a trivial set of the Name property (to its |
| // current value) but an attempt to set the property to a different value |
| // fails. |
| { |
| Error error; |
| service_->mutable_store()->SetAnyProperty( |
| kNameProperty, brillo::Any(GetFriendlyName()), &error); |
| EXPECT_FALSE(error.IsFailure()); |
| } |
| { |
| Error error; |
| service_->mutable_store()->SetAnyProperty( |
| kNameProperty, PropertyStoreTest::kStringV, &error); |
| ASSERT_TRUE(error.IsFailure()); |
| EXPECT_EQ(Error::kInvalidArguments, error.type()); |
| } |
| } |
| |
| TEST_F(ServiceTest, GetLoadableStorageIdentifier) { |
| FakeStore storage; |
| EXPECT_EQ("", service_->GetLoadableStorageIdentifier(storage)); |
| // Setting any property will add an entry for |storage_id_|. |
| storage.SetString(storage_id_, Service::kStorageGUID, kGUID); |
| EXPECT_EQ(storage_id_, service_->GetLoadableStorageIdentifier(storage)); |
| } |
| |
| TEST_F(ServiceTest, IsLoadableFrom) { |
| FakeStore storage; |
| EXPECT_FALSE(service_->IsLoadableFrom(storage)); |
| // Setting any property will add an entry for |storage_id_|. |
| storage.SetString(storage_id_, Service::kStorageGUID, kGUID); |
| EXPECT_TRUE(service_->IsLoadableFrom(storage)); |
| } |
| |
| class ServiceWithOnEapCredentialsChangedOverride : public ServiceUnderTest { |
| public: |
| ServiceWithOnEapCredentialsChangedOverride(Manager* manager, |
| EapCredentials* eap) |
| : ServiceUnderTest(manager) { |
| SetEapCredentials(eap); |
| } |
| void OnEapCredentialsChanged(Service::UpdateCredentialsReason) override { |
| SetHasEverConnected(false); |
| } |
| }; |
| |
| TEST_F(ServiceTest, LoadTrafficCounters) { |
| FakeStore storage; |
| const uint64_t kUserRxBytes = 1234; |
| const uint64_t kChromeTxPackets = 9876; |
| patchpanel::Client::TrafficVector kUserCounters = {.rx_bytes = kUserRxBytes, |
| .tx_bytes = 0, |
| .rx_packets = 0, |
| .tx_packets = 0}; |
| patchpanel::Client::TrafficVector kChromeCounters = { |
| .rx_bytes = 0, |
| .tx_bytes = 0, |
| .rx_packets = 0, |
| .tx_packets = kChromeTxPackets}; |
| storage.SetUint64(storage_id_, |
| Service::GetCurrentTrafficCounterKey( |
| patchpanel::Client::TrafficSource::kUser, |
| Service::kStorageTrafficCounterRxBytesSuffix), |
| kUserRxBytes); |
| storage.SetUint64(storage_id_, |
| Service::GetCurrentTrafficCounterKey( |
| patchpanel::Client::TrafficSource::kChrome, |
| Service::kStorageTrafficCounterTxPacketsSuffix), |
| kChromeTxPackets); |
| EXPECT_TRUE(service_->Load(&storage)); |
| EXPECT_EQ(service_->current_traffic_counters() |
| [patchpanel::Client::TrafficSource::kUser], |
| kUserCounters); |
| EXPECT_EQ(service_->current_traffic_counters() |
| [patchpanel::Client::TrafficSource::kChrome], |
| kChromeCounters); |
| } |
| |
| TEST_F(ServiceTest, Load) { |
| FakeStore storage; |
| const int kPriority = 20; |
| const std::string kProxyConfig("proxy-config"); |
| const std::string kUIData("ui-data"); |
| storage.SetString(storage_id_, Service::kStorageCheckPortal, "false"); |
| storage.SetString(storage_id_, Service::kStorageGUID, kGUID); |
| storage.SetBool(storage_id_, Service::kStorageHasEverConnected, true); |
| storage.SetInt(storage_id_, Service::kStoragePriority, kPriority); |
| storage.SetString(storage_id_, Service::kStorageProxyConfig, kProxyConfig); |
| storage.SetString(storage_id_, Service::kStorageUIData, kUIData); |
| |
| EXPECT_TRUE(service_->Load(&storage)); |
| EXPECT_EQ(Service::CheckPortalState::kFalse, service_->check_portal()); |
| EXPECT_EQ("false", service_->GetCheckPortal(nullptr)); |
| EXPECT_EQ(kGUID, service_->guid_); |
| EXPECT_TRUE(service_->has_ever_connected_); |
| EXPECT_EQ(kProxyConfig, service_->proxy_config_); |
| EXPECT_EQ(kUIData, service_->ui_data_); |
| |
| // Removing the storage entry should cause the service to fail to load. |
| storage.DeleteGroup(storage_id_); |
| EXPECT_FALSE(service_->Load(&storage)); |
| |
| // Set an empty GUID to add a storage entry so that the service loads. |
| storage.SetString(storage_id_, Service::kStorageGUID, ""); |
| |
| // Assure that parameters are set to default if not available in the profile. |
| EXPECT_TRUE(service_->Load(&storage)); |
| EXPECT_EQ(Service::CheckPortalState::kAutomatic, service_->check_portal()); |
| EXPECT_EQ("auto", service_->GetCheckPortal(nullptr)); |
| EXPECT_EQ("", service_->guid_); |
| EXPECT_EQ("", service_->proxy_config_); |
| EXPECT_EQ("", service_->ui_data_); |
| } |
| |
| TEST_F(ServiceTest, LoadEap) { |
| FakeStore storage; |
| |
| const char kIdentity[] = "identity"; |
| storage.SetString(storage_id_, EapCredentials::kStorageCredentialEapIdentity, |
| kIdentity); |
| |
| auto* eap = new EapCredentials(); |
| ASSERT_FALSE(service2_->eap()); |
| service2_->SetEapCredentials(eap); |
| service2_->SetHasEverConnected(true); |
| EXPECT_TRUE(service2_->has_ever_connected()); |
| |
| EXPECT_TRUE(service2_->Load(&storage)); |
| EXPECT_EQ(service2_->eap()->identity(), kIdentity); |
| |
| std::string identity; |
| EXPECT_TRUE(storage.GetString( |
| storage_id_, EapCredentials::kStorageCredentialEapIdentity, &identity)); |
| EXPECT_EQ(identity, kIdentity); |
| |
| // has_ever_connected_ is unaffected when loading eap credentials. |
| EXPECT_TRUE(service2_->has_ever_connected()); |
| } |
| |
| TEST_F(ServiceTest, GetEapPassphrase) { |
| std::string kPassword = "random-string"; |
| auto* eap = new EapCredentials(); |
| eap->set_password(kPassword); |
| service2_->SetEapCredentials(eap); |
| |
| Error error; |
| std::string password = service2_->GetEapPassphrase(&error); |
| EXPECT_EQ(password, kPassword); |
| } |
| |
| TEST_F(ServiceTest, GetEapPassphraseNonEap) { |
| Error error; |
| std::string password = service_->GetEapPassphrase(&error); |
| EXPECT_TRUE(password.empty()); |
| EXPECT_FALSE(error.IsSuccess()); |
| } |
| |
| TEST_F(ServiceTest, LoadFail) { |
| FakeStore storage; |
| EXPECT_FALSE(service_->Load(&storage)); |
| } |
| |
| TEST_F(ServiceTest, LoadAutoConnect) { |
| FakeStore storage; |
| storage.SetString(storage_id_, Service::kStorageGUID, kGUID); |
| |
| // AutoConnect is unset. |
| EXPECT_TRUE(service_->Load(&storage)); |
| EXPECT_FALSE(service_->auto_connect()); |
| EXPECT_FALSE(service_->retain_auto_connect()); |
| |
| // AutoConnect is false. |
| storage.SetBool(storage_id_, Service::kStorageAutoConnect, false); |
| EXPECT_TRUE(service_->Load(&storage)); |
| EXPECT_FALSE(service_->auto_connect()); |
| EXPECT_TRUE(service_->retain_auto_connect()); |
| |
| // AutoConnect is true. |
| storage.SetBool(storage_id_, Service::kStorageAutoConnect, true); |
| EXPECT_TRUE(service_->Load(&storage)); |
| EXPECT_TRUE(service_->auto_connect()); |
| EXPECT_TRUE(service_->retain_auto_connect()); |
| } |
| |
| TEST_F(ServiceTest, SaveString) { |
| FakeStore storage; |
| static const char kKey[] = "test-key"; |
| static const char kData[] = "test-data"; |
| |
| // Test setting kKey to a value. |
| service_->SaveStringOrClear(&storage, storage_id_, kKey, kData); |
| std::string data; |
| EXPECT_TRUE(storage.GetString(storage_id_, kKey, &data)); |
| EXPECT_EQ(data, kData); |
| |
| // Setting kKey to an empty value should delete the entry |
| service_->SaveStringOrClear(&storage, storage_id_, kKey, ""); |
| EXPECT_FALSE(storage.GetString(storage_id_, kKey, &data)); |
| } |
| |
| TEST_F(ServiceTest, SaveTrafficCounters) { |
| FakeStore storage; |
| patchpanel::Client::TrafficVector kVPNCounters = { |
| .rx_bytes = 0, .tx_bytes = 19, .rx_packets = 28, .tx_packets = 0}; |
| service_ |
| ->current_traffic_counters()[patchpanel::Client::TrafficSource::kVpn] = |
| kVPNCounters; |
| EXPECT_TRUE(service_->Save(&storage)); |
| patchpanel::Client::TrafficVector kActualVPNCounters; |
| storage.GetUint64(storage_id_, |
| Service::GetCurrentTrafficCounterKey( |
| patchpanel::Client::TrafficSource::kVpn, |
| Service::kStorageTrafficCounterRxBytesSuffix), |
| &kActualVPNCounters.rx_bytes); |
| storage.GetUint64(storage_id_, |
| Service::GetCurrentTrafficCounterKey( |
| patchpanel::Client::TrafficSource::kVpn, |
| Service::kStorageTrafficCounterTxBytesSuffix), |
| &kActualVPNCounters.tx_bytes); |
| storage.GetUint64(storage_id_, |
| Service::GetCurrentTrafficCounterKey( |
| patchpanel::Client::TrafficSource::kVpn, |
| Service::kStorageTrafficCounterRxPacketsSuffix), |
| &kActualVPNCounters.rx_packets); |
| storage.GetUint64(storage_id_, |
| Service::GetCurrentTrafficCounterKey( |
| patchpanel::Client::TrafficSource::kVpn, |
| Service::kStorageTrafficCounterTxPacketsSuffix), |
| &kActualVPNCounters.tx_packets); |
| EXPECT_EQ(kVPNCounters, kActualVPNCounters); |
| } |
| |
| TEST_F(ServiceTest, Save) { |
| FakeStore storage; |
| service_->technology_ = Technology::kWiFi; |
| EXPECT_TRUE(service_->Save(&storage)); |
| |
| std::string type; |
| EXPECT_TRUE(storage.GetString(storage_id_, Service::kStorageType, &type)); |
| EXPECT_EQ(type, service_->GetTechnologyName()); |
| } |
| |
| TEST_F(ServiceTest, SaveEap) { |
| FakeStore storage; |
| const char kIdentity[] = "identity"; |
| auto* eap = new EapCredentials(); |
| eap->set_identity(kIdentity); |
| ASSERT_FALSE(service2_->eap()); |
| service2_->SetEapCredentials(eap); |
| service2_->set_save_credentials(true); |
| EXPECT_TRUE(service2_->Save(&storage)); |
| |
| std::string identity; |
| EXPECT_TRUE(storage.GetString( |
| storage_id_, EapCredentials::kStorageCredentialEapIdentity, &identity)); |
| EXPECT_EQ(identity, kIdentity); |
| } |
| |
| TEST_F(ServiceTest, RetainAutoConnect) { |
| FakeStore storage; |
| |
| // AutoConnect flag set true. |
| service_->EnableAndRetainAutoConnect(); |
| EXPECT_TRUE(service_->Save(&storage)); |
| bool auto_connect = false; |
| EXPECT_TRUE(storage.GetBool(storage_id_, Service::kStorageAutoConnect, |
| &auto_connect)); |
| EXPECT_TRUE(auto_connect); |
| |
| // AutoConnect flag set false. |
| service_->SetAutoConnect(false); |
| EXPECT_TRUE(service_->Save(&storage)); |
| EXPECT_TRUE(storage.GetBool(storage_id_, Service::kStorageAutoConnect, |
| &auto_connect)); |
| EXPECT_FALSE(auto_connect); |
| } |
| |
| TEST_F(ServiceTest, HasEverConnectedSavedToProfile) { |
| FakeStore storage; |
| |
| // HasEverConnected flag set true. |
| service_->SetHasEverConnected(true); |
| EXPECT_TRUE(service_->Save(&storage)); |
| bool has_ever_connected = false; |
| EXPECT_TRUE(storage.GetBool(storage_id_, Service::kStorageHasEverConnected, |
| &has_ever_connected)); |
| EXPECT_TRUE(has_ever_connected); |
| |
| // HasEverConnected flag set false. |
| service_->SetHasEverConnected(false); |
| EXPECT_TRUE(service_->Save(&storage)); |
| EXPECT_TRUE(storage.GetBool(storage_id_, Service::kStorageHasEverConnected, |
| &has_ever_connected)); |
| EXPECT_FALSE(has_ever_connected); |
| } |
| |
| TEST_F(ServiceTest, Unload) { |
| FakeStore storage; |
| storage.SetString(storage_id_, Service::kStorageGUID, kGUID); |
| storage.SetBool(storage_id_, Service::kStorageHasEverConnected, true); |
| |
| EXPECT_FALSE(service_->explicitly_disconnected_); |
| EXPECT_FALSE(service_->has_ever_connected_); |
| service_->explicitly_disconnected_ = true; |
| |
| EXPECT_TRUE(service_->Load(&storage)); |
| |
| EXPECT_EQ(kGUID, service_->guid_); |
| EXPECT_FALSE(service_->explicitly_disconnected_); |
| EXPECT_TRUE(service_->has_ever_connected_); |
| |
| service_->explicitly_disconnected_ = true; |
| SetStateField(Service::kStateConnected); |
| service_->Unload(); |
| EXPECT_EQ(std::string(""), service_->guid_); |
| EXPECT_FALSE(service_->explicitly_disconnected_); |
| EXPECT_FALSE(service_->has_ever_connected_); |
| EXPECT_EQ(1, service_->disconnect_calls()); |
| } |
| |
| TEST_F(ServiceTest, SaveAndLoadConnectionTimestamps) { |
| FakeStore storage; |
| base::Time time1, time2, time3; |
| ASSERT_TRUE(base::Time::FromString("01 Jan 2018 12:00:00", &time1)); |
| ASSERT_TRUE(base::Time::FromString("02 Jan 2018 12:00:00", &time2)); |
| ASSERT_TRUE(base::Time::FromString("03 Jan 2018 12:00:00", &time3)); |
| service_->SetLastConnectedProperty(time1); |
| service_->SetLastManualConnectAttemptProperty(time2); |
| service_->SetLastOnlineProperty(time3); |
| EXPECT_TRUE(service_->Save(&storage)); |
| |
| // Verify the values in the storage |
| uint64_t time_ms; |
| EXPECT_TRUE( |
| storage.GetUint64(storage_id_, Service::kStorageLastConnected, &time_ms)); |
| EXPECT_EQ(time_ms, time1.ToDeltaSinceWindowsEpoch().InMilliseconds()); |
| |
| EXPECT_TRUE(storage.GetUint64( |
| storage_id_, Service::kStorageLastManualConnectAttempt, &time_ms)); |
| EXPECT_EQ(time_ms, time2.ToDeltaSinceWindowsEpoch().InMilliseconds()); |
| |
| EXPECT_TRUE( |
| storage.GetUint64(storage_id_, Service::kStorageLastOnline, &time_ms)); |
| EXPECT_EQ(time_ms, time3.ToDeltaSinceWindowsEpoch().InMilliseconds()); |
| |
| // Load into a separate service |
| EXPECT_TRUE(service2_->Load(&storage)); |
| EXPECT_EQ(time1, base::Time::FromDeltaSinceWindowsEpoch(base::Milliseconds( |
| service2_->GetLastConnectedProperty(nullptr)))); |
| EXPECT_EQ(time2, |
| base::Time::FromDeltaSinceWindowsEpoch(base::Milliseconds( |
| service2_->GetLastManualConnectAttemptProperty(nullptr)))); |
| EXPECT_EQ(time3, base::Time::FromDeltaSinceWindowsEpoch(base::Milliseconds( |
| service2_->GetLastOnlineProperty(nullptr)))); |
| } |
| |
| // Tests that callback is invoked properly when static IP configs changed. |
| TEST_F(ServiceTest, StaticIPConfigsChanged) { |
| constexpr char kTestIpAddress1[] = "1.2.3.4"; |
| constexpr char kTestIpAddress2[] = "1.2.3.5"; |
| constexpr int kTestPrefix = 24; |
| KeyValueStore static_ip_configs; |
| FakeStore storage; |
| |
| const auto update_address = [&](const std::string& ip_addr) { |
| static_ip_configs.Set(kAddressProperty, ip_addr); |
| static_ip_configs.Set(kPrefixlenProperty, kTestPrefix); |
| service_->mutable_store()->SetKeyValueStoreProperty( |
| kStaticIPConfigProperty, static_ip_configs, /*error=*/nullptr); |
| }; |
| update_address(kTestIpAddress1); |
| ASSERT_TRUE(service_->Save(&storage)); |
| |
| EXPECT_EQ(0, service_->GetNetworkID(nullptr)); |
| |
| int fake_network_id = 335; |
| EXPECT_CALL(*GetAdaptor(), |
| EmitIntChanged(kNetworkIDProperty, fake_network_id)); |
| auto network = |
| std::make_unique<MockNetwork>(1, "test_ifname", Technology::kEthernet); |
| ON_CALL(*network, network_id).WillByDefault(Return(fake_network_id)); |
| service_->AttachNetwork(network->AsWeakPtr()); |
| SetStateField(Service::kStateConnected); |
| |
| // Changes the address, network should be notified. |
| EXPECT_CALL(*network, OnStaticIPConfigChanged); |
| update_address(kTestIpAddress2); |
| // Address is not changed, network should not be notified. |
| update_address(kTestIpAddress2); |
| // Reloads the service, network should be notified since address is changed. |
| EXPECT_CALL(*network, OnStaticIPConfigChanged); |
| ASSERT_TRUE(service_->Load(&storage)); |
| |
| EXPECT_EQ(fake_network_id, service_->GetNetworkID(nullptr)); |
| |
| // Persists the service and reloads again, network should not be notified |
| // since address is not changed. |
| ASSERT_TRUE(service_->Save(&storage)); |
| // Detaches the network, it should be notified once, but not any more. |
| EXPECT_CALL(*network, OnStaticIPConfigChanged); |
| EXPECT_CALL(*GetAdaptor(), EmitIntChanged(kNetworkIDProperty, 0)); |
| service_->DetachNetwork(); |
| update_address(kTestIpAddress2); |
| |
| EXPECT_EQ(0, service_->GetNetworkID(nullptr)); |
| } |
| |
| TEST_F(ServiceTest, State) { |
| EXPECT_EQ(Service::kStateIdle, service_->state()); |
| EXPECT_EQ(Service::kStateIdle, GetPreviousState()); |
| EXPECT_EQ(Service::kFailureNone, service_->failure()); |
| const std::string no_error( |
| Service::ConnectFailureToString(Service::kFailureNone)); |
| EXPECT_EQ(no_error, service_->error()); |
| |
| EXPECT_CALL(*GetAdaptor(), EmitStringChanged(kStateProperty, _)).Times(6); |
| EXPECT_CALL(*GetAdaptor(), EmitStringChanged(kErrorProperty, _)).Times(4); |
| EXPECT_CALL(mock_manager_, UpdateService(Pointer(service_))); |
| service_->SetState(Service::kStateConnected); |
| EXPECT_EQ(Service::kStateIdle, GetPreviousState()); |
| // A second state change shouldn't cause another update |
| service_->SetState(Service::kStateConnected); |
| EXPECT_EQ(Service::kStateConnected, service_->state()); |
| EXPECT_EQ(Service::kStateIdle, GetPreviousState()); |
| EXPECT_EQ(Service::kFailureNone, service_->failure()); |
| EXPECT_TRUE(service_->has_ever_connected_); |
| |
| EXPECT_CALL(mock_manager_, UpdateService(Pointer(service_))); |
| service_->SetFailure(Service::kFailureOutOfRange); |
| EXPECT_TRUE(service_->IsFailed()); |
| std::optional<base::TimeDelta> time_failed = GetTimeSinceFailed(); |
| ASSERT_TRUE(time_failed); |
| EXPECT_GT(*time_failed, base::TimeDelta()); |
| EXPECT_GT(service_->previous_error_serial_number_, 0); |
| EXPECT_EQ(Service::kStateFailure, service_->state()); |
| EXPECT_EQ(Service::kFailureOutOfRange, service_->failure()); |
| const std::string out_of_range_error( |
| Service::ConnectFailureToString(Service::kFailureOutOfRange)); |
| EXPECT_EQ(out_of_range_error, service_->error()); |
| EXPECT_EQ(out_of_range_error, service_->previous_error_); |
| |
| EXPECT_CALL(mock_manager_, UpdateService(Pointer(service_))); |
| service_->SetState(Service::kStateConnected); |
| EXPECT_FALSE(service_->IsFailed()); |
| EXPECT_FALSE(GetTimeSinceFailed()); |
| EXPECT_EQ(no_error, service_->error()); |
| EXPECT_EQ(out_of_range_error, service_->previous_error_); |
| EXPECT_GT(service_->previous_error_serial_number_, 0); |
| |
| EXPECT_CALL(mock_manager_, UpdateService(Pointer(service_))); |
| service_->SetFailureSilent(Service::kFailurePinMissing); |
| EXPECT_TRUE(service_->IsFailed()); |
| time_failed = GetTimeSinceFailed(); |
| ASSERT_TRUE(time_failed); |
| EXPECT_GT(*time_failed, base::TimeDelta()); |
| EXPECT_GT(service_->previous_error_serial_number_, 0); |
| EXPECT_EQ(Service::kStateIdle, service_->state()); |
| EXPECT_EQ(Service::kFailurePinMissing, service_->failure()); |
| const std::string pin_missing_error( |
| Service::ConnectFailureToString(Service::kFailurePinMissing)); |
| EXPECT_EQ(pin_missing_error, service_->error()); |
| EXPECT_EQ(pin_missing_error, service_->previous_error_); |
| |
| // If the Service has a Profile, the profile should be saved when |
| // the service enters kStateConnected. (The case where the service |
| // doesn't have a profile is tested above.) |
| MockProfileRefPtr mock_profile(new MockProfile(&mock_manager_)); |
| FakeStore storage; |
| service_->set_profile(mock_profile); |
| service_->has_ever_connected_ = false; |
| EXPECT_CALL(mock_manager_, UpdateService(Pointer(service_))); |
| EXPECT_CALL(*mock_profile, GetConstStorage()).WillOnce(Return(&storage)); |
| EXPECT_CALL(*mock_profile, UpdateService(Pointer(service_))); |
| service_->SetState(Service::kStateConnected); |
| EXPECT_TRUE(service_->has_ever_connected_); |
| service_->set_profile(nullptr); // Break reference cycle. |
| |
| // Similar to the above, but emulate an emphemeral profile, which |
| // has no storage. We can't update the service in the profile, but |
| // we should not crash. |
| service_->state_ = Service::kStateIdle; // Skips state change logic. |
| service_->set_profile(mock_profile); |
| service_->has_ever_connected_ = false; |
| EXPECT_CALL(mock_manager_, UpdateService(Pointer(service_))); |
| EXPECT_CALL(*mock_profile, GetConstStorage()).WillOnce(Return(nullptr)); |
| service_->SetState(Service::kStateConnected); |
| EXPECT_TRUE(service_->has_ever_connected_); |
| service_->set_profile(nullptr); // Break reference cycle. |
| } |
| |
| TEST_F(ServiceTest, StateResetAfterFailure) { |
| service_->SetFailure(Service::kFailureOutOfRange); |
| EXPECT_EQ(Service::kStateFailure, service_->state()); |
| Error error; |
| service_->Connect(&error, "in test"); |
| EXPECT_EQ(Service::kStateIdle, service_->state()); |
| EXPECT_EQ(Service::kFailureNone, service_->failure()); |
| |
| service_->SetState(Service::kStateConnected); |
| service_->Connect(&error, "in test"); |
| EXPECT_EQ(Service::kStateConnected, service_->state()); |
| } |
| |
| TEST_F(ServiceTest, UserInitiatedConnectionAttempt) { |
| { |
| // The initiation of connection attempt by user is successful. |
| Error error; |
| service_->SetState(Service::kStateIdle); |
| service_->UserInitiatedConnect(kConnectDisconnectReason, &error); |
| EXPECT_TRUE(service_->is_in_user_connect()); |
| } |
| { |
| // The initiation of connection attempt by user fails, i.e. not attempted. |
| // |Service::UserInitiatedConnect| checks if |error| is set during the call |
| // stack through |Service::Connect| to know if the attempt is initiated |
| // successfully. Here write a failure type in |error| to mimic this case. |
| Error error(Error::kOperationFailed); |
| service_->SetState(Service::kStateIdle); |
| service_->UserInitiatedConnect(kConnectDisconnectReason, &error); |
| EXPECT_FALSE(service_->is_in_user_connect()); |
| } |
| } |
| |
| TEST_F(ServiceTest, UserInitiatedConnectionResult) { |
| service_->technology_ = Technology::kWiFi; |
| Error error; |
| // User-initiated connection attempt succeed. |
| service_->SetState(Service::kStateIdle); |
| service_->UserInitiatedConnect(kConnectDisconnectReason, &error); |
| EXPECT_CALL(*metrics(), |
| SendEnumToUMA(Metrics::kMetricWifiUserInitiatedConnectionResult, |
| Metrics::kUserInitiatedConnectionResultSuccess)); |
| EXPECT_CALL(*metrics(), |
| SendEnumToUMA( |
| Metrics::kMetricWifiUserInitiatedConnectionFailureReason, _)) |
| .Times(0); |
| service_->SetState(Service::kStateConnected); |
| Mock::VerifyAndClearExpectations(metrics()); |
| |
| // User-initiated connection attempt failed. |
| service_->SetState(Service::kStateIdle); |
| service_->UserInitiatedConnect(kConnectDisconnectReason, &error); |
| EXPECT_CALL(*metrics(), |
| SendEnumToUMA(Metrics::kMetricWifiUserInitiatedConnectionResult, |
| Metrics::kUserInitiatedConnectionResultFailure)); |
| EXPECT_CALL( |
| *metrics(), |
| SendEnumToUMA(Metrics::kMetricWifiUserInitiatedConnectionFailureReason, |
| Metrics::kUserInitiatedConnectionFailureReasonDHCP)); |
| service_->SetFailure(Service::kFailureDHCP); |
| Mock::VerifyAndClearExpectations(metrics()); |
| |
| // User-initiated connection attempt aborted. |
| service_->SetState(Service::kStateIdle); |
| service_->UserInitiatedConnect(kConnectDisconnectReason, &error); |
| service_->SetState(Service::kStateAssociating); |
| EXPECT_CALL(*metrics(), |
| SendEnumToUMA(Metrics::kMetricWifiUserInitiatedConnectionResult, |
| Metrics::kUserInitiatedConnectionResultAborted)); |
| EXPECT_CALL(*metrics(), |
| SendEnumToUMA( |
| Metrics::kMetricWifiUserInitiatedConnectionFailureReason, _)) |
| .Times(0); |
| service_->SetState(Service::kStateIdle); |
| Mock::VerifyAndClearExpectations(metrics()); |
| |
| // No metric reporting for other state transition. |
| service_->SetState(Service::kStateIdle); |
| service_->UserInitiatedConnect(kConnectDisconnectReason, &error); |
| EXPECT_CALL( |
| *metrics(), |
| SendEnumToUMA(Metrics::kMetricWifiUserInitiatedConnectionResult, _)) |
| .Times(0); |
| EXPECT_CALL(*metrics(), |
| SendEnumToUMA( |
| Metrics::kMetricWifiUserInitiatedConnectionFailureReason, _)) |
| .Times(0); |
| service_->SetState(Service::kStateAssociating); |
| service_->SetState(Service::kStateConfiguring); |
| Mock::VerifyAndClearExpectations(metrics()); |
| |
| // No metric reporting for non-user-initiated connection. |
| service_->SetState(Service::kStateIdle); |
| service_->Connect(&error, "in test"); |
| EXPECT_CALL( |
| *metrics(), |
| SendEnumToUMA(Metrics::kMetricWifiUserInitiatedConnectionResult, _)) |
| .Times(0); |
| EXPECT_CALL(*metrics(), |
| SendEnumToUMA( |
| Metrics::kMetricWifiUserInitiatedConnectionFailureReason, _)) |
| .Times(0); |
| service_->SetState(Service::kStateConnected); |
| Mock::VerifyAndClearExpectations(metrics()); |
| |
| // No metric reporting for other technology. |
| service_->technology_ = Technology::kCellular; |
| service_->SetState(Service::kStateIdle); |
| service_->UserInitiatedConnect(kConnectDisconnectReason, &error); |
| EXPECT_CALL( |
| *metrics(), |
| SendEnumToUMA(Metrics::kMetricWifiUserInitiatedConnectionResult, _)) |
| .Times(0); |
| EXPECT_CALL(*metrics(), |
| SendEnumToUMA( |
| Metrics::kMetricWifiUserInitiatedConnectionFailureReason, _)) |
| .Times(0); |
| service_->SetFailure(Service::kFailureDHCP); |
| Mock::VerifyAndClearExpectations(metrics()); |
| } |
| |
| TEST_F(ServiceTest, CompleteCellularActivation) { |
| Error error; |
| service_->CompleteCellularActivation(&error); |
| EXPECT_EQ(Error::kNotImplemented, error.type()); |
| } |
| |
| TEST_F(ServiceTest, EnableAndRetainAutoConnect) { |
| EXPECT_FALSE(service_->retain_auto_connect()); |
| EXPECT_FALSE(service_->auto_connect()); |
| |
| service_->EnableAndRetainAutoConnect(); |
| EXPECT_TRUE(service_->retain_auto_connect()); |
| EXPECT_TRUE(service_->auto_connect()); |
| } |
| |
| TEST_F(ServiceTest, ReRetainAutoConnect) { |
| service_->EnableAndRetainAutoConnect(); |
| EXPECT_TRUE(service_->retain_auto_connect()); |
| EXPECT_TRUE(service_->auto_connect()); |
| |
| service_->SetAutoConnect(false); |
| service_->EnableAndRetainAutoConnect(); |
| EXPECT_TRUE(service_->retain_auto_connect()); |
| EXPECT_FALSE(service_->auto_connect()); |
| } |
| |
| TEST_F(ServiceTest, IsAutoConnectable) { |
| const char* reason = nullptr; |
| service_->SetConnectable(true); |
| |
| // Services with non-primary connectivity technologies should not auto-connect |
| // when the system is offline. |
| service_->technology_ = Technology::kUnknown; |
| EXPECT_EQ(Technology::kUnknown, service_->technology()); |
| EXPECT_CALL(mock_manager_, IsConnected()).WillOnce(Return(false)); |
| EXPECT_FALSE(service_->IsAutoConnectable(&reason)); |
| EXPECT_STREQ(Service::kAutoConnOffline, reason); |
| |
| service_->technology_ = Technology::kEthernet; |
| EXPECT_TRUE(service_->IsAutoConnectable(&reason)); |
| |
| // We should not auto-connect to a Service that a user has |
| // deliberately disconnected. |
| Error error; |
| service_->UserInitiatedDisconnect(kConnectDisconnectReason, &error); |
| EXPECT_FALSE(service_->IsAutoConnectable(&reason)); |
| EXPECT_STREQ(Service::kAutoConnExplicitDisconnect, reason); |
| |
| // But if the Service is reloaded, it is eligible for auto-connect |
| // again. |
| FakeStore storage; |
| storage.SetString(storage_id_, Service::kStorageGUID, kGUID); |
| EXPECT_TRUE(service_->Load(&storage)); |
| EXPECT_TRUE(service_->IsAutoConnectable(&reason)); |
| |
| // A deliberate Connect should also re-enable auto-connect. |
| service_->UserInitiatedDisconnect(kConnectDisconnectReason, &error); |
| EXPECT_FALSE(service_->IsAutoConnectable(&reason)); |
| service_->Connect(&error, "in test"); |
| EXPECT_TRUE(service_->IsAutoConnectable(&reason)); |
| |
| // A non-user initiated Disconnect doesn't change anything. |
| service_->Disconnect(&error, "in test"); |
| EXPECT_TRUE(service_->IsAutoConnectable(&reason)); |
| |
| // A resume also re-enables auto-connect. |
| service_->UserInitiatedDisconnect(kConnectDisconnectReason, &error); |
| EXPECT_FALSE(service_->IsAutoConnectable(&reason)); |
| service_->OnAfterResume(); |
| EXPECT_TRUE(service_->IsAutoConnectable(&reason)); |
| |
| service_->SetState(Service::kStateConnected); |
| EXPECT_FALSE(service_->IsAutoConnectable(&reason)); |
| EXPECT_STREQ(Service::kAutoConnConnected, reason); |
| |
| service_->SetState(Service::kStateAssociating); |
| EXPECT_FALSE(service_->IsAutoConnectable(&reason)); |
| EXPECT_STREQ(Service::kAutoConnConnecting, reason); |
| |
| service_->SetState(Service::kStateIdle); |
| EXPECT_CALL(mock_manager_, |
| IsTechnologyAutoConnectDisabled(service_->technology_)) |
| .WillOnce(Return(true)); |
| EXPECT_FALSE(service_->IsAutoConnectable(&reason)); |
| EXPECT_STREQ(Service::kAutoConnTechnologyNotAutoConnectable, reason); |
| } |
| |
| TEST_F(AllMockServiceTest, AutoConnectWithFailures) { |
| const char* reason; |
| service_->SetConnectable(true); |
| service_->technology_ = Technology::kEthernet; |
| EXPECT_TRUE(service_->IsAutoConnectable(&reason)); |
| |
| // The very first AutoConnect() doesn't trigger any throttling. |
| EXPECT_CALL(dispatcher_, PostDelayedTask(_, _, _)).Times(0); |
| service_->AutoConnect(); |
| Mock::VerifyAndClearExpectations(&dispatcher_); |
| EXPECT_TRUE(service_->IsAutoConnectable(&reason)); |
| |
| // The second call does trigger some throttling. |
| EXPECT_CALL(dispatcher_, |
| PostDelayedTask(_, _, service_->GetMinAutoConnectCooldownTime())); |
| service_->AutoConnect(); |
| Mock::VerifyAndClearExpectations(&dispatcher_); |
| EXPECT_FALSE(service_->IsAutoConnectable(&reason)); |
| EXPECT_STREQ(Service::kAutoConnThrottled, reason); |
| |
| // Calling AutoConnect() again before the cooldown terminates does not change |
| // the timeout. |
| EXPECT_CALL(dispatcher_, PostDelayedTask(_, _, _)).Times(0); |
| service_->AutoConnect(); |
| Mock::VerifyAndClearExpectations(&dispatcher_); |
| EXPECT_FALSE(service_->IsAutoConnectable(&reason)); |
| EXPECT_STREQ(Service::kAutoConnThrottled, reason); |
| |
| // Once the timeout expires, we can AutoConnect() again. |
| service_->ReEnableAutoConnectTask(); |
| EXPECT_TRUE(service_->IsAutoConnectable(&reason)); |
| |
| // Timeouts increase exponentially. |
| base::TimeDelta next_cooldown_time = service_->auto_connect_cooldown_; |
| EXPECT_EQ(next_cooldown_time, Service::kAutoConnectCooldownBackoffFactor * |
| service_->GetMinAutoConnectCooldownTime()); |
| while (next_cooldown_time <= service_->GetMaxAutoConnectCooldownTime()) { |
| EXPECT_CALL(dispatcher_, PostDelayedTask(_, _, next_cooldown_time)); |
| service_->AutoConnect(); |
| Mock::VerifyAndClearExpectations(&dispatcher_); |
| EXPECT_FALSE(service_->IsAutoConnectable(&reason)); |
| EXPECT_STREQ(Service::kAutoConnThrottled, reason); |
| service_->ReEnableAutoConnectTask(); |
| next_cooldown_time *= Service::kAutoConnectCooldownBackoffFactor; |
| } |
| |
| // Once we hit our cap, future timeouts are the same. |
| for (int32_t i = 0; i < 2; i++) { |
| EXPECT_CALL( |
| dispatcher_, |
| PostDelayedTask(_, _, service_->GetMaxAutoConnectCooldownTime())); |
| service_->AutoConnect(); |
| Mock::VerifyAndClearExpectations(&dispatcher_); |
| EXPECT_FALSE(service_->IsAutoConnectable(&reason)); |
| EXPECT_STREQ(Service::kAutoConnThrottled, reason); |
| service_->ReEnableAutoConnectTask(); |
| } |
| |
| // Connecting successfully resets our cooldown. |
| service_->SetState(Service::kStateConnected); |
| service_->SetState(Service::kStateIdle); |
| reason = ""; |
| EXPECT_TRUE(service_->IsAutoConnectable(&reason)); |
| EXPECT_STREQ("", reason); |
| EXPECT_TRUE(service_->auto_connect_cooldown_.is_zero()); |
| |
| // But future AutoConnects behave as before |
| EXPECT_CALL(dispatcher_, |
| PostDelayedTask(_, _, service_->GetMinAutoConnectCooldownTime())) |
| .Times(1); |
| service_->AutoConnect(); |
| service_->AutoConnect(); |
| Mock::VerifyAndClearExpectations(&dispatcher_); |
| EXPECT_FALSE(service_->IsAutoConnectable(&reason)); |
| EXPECT_STREQ(Service::kAutoConnThrottled, reason); |
| |
| // Cooldowns are forgotten if we go through a suspend/resume cycle. |
| service_->OnAfterResume(); |
| reason = ""; |
| EXPECT_TRUE(service_->IsAutoConnectable(&reason)); |
| EXPECT_STREQ("", reason); |
| } |
| |
| TEST_F(ServiceTest, SkipAutoConnectAfterRecentBadPassphraseFailure) { |
| const char* reason; |
| service_->SetConnectable(true); |
| SetTechnology(Technology::kWiFi); |
| EXPECT_TRUE(IsAutoConnectable(&reason)); |
| |
| service_->set_failed_time_for_testing(base::Time::Now()); |
| service_->set_previous_error_for_testing(kErrorBadPassphrase); |
| service_->AutoConnect(); |
| EXPECT_EQ(0, service_->connect_calls()); |
| } |
| |
| TEST_F(ServiceTest, ConfigureBadProperty) { |
| KeyValueStore args; |
| args.Set<std::string>("XXXInvalid", "Value"); |
| Error error; |
| service_->Configure(args, &error); |
| EXPECT_FALSE(error.IsSuccess()); |
| } |
| |
| TEST_F(ServiceTest, ConfigureBoolProperty) { |
| service_->EnableAndRetainAutoConnect(); |
| service_->SetAutoConnect(false); |
| ASSERT_FALSE(service_->auto_connect()); |
| KeyValueStore args; |
| args.Set<bool>(kAutoConnectProperty, true); |
| Error error; |
| service_->Configure(args, &error); |
| EXPECT_TRUE(error.IsSuccess()); |
| EXPECT_TRUE(service_->auto_connect()); |
| } |
| |
| TEST_F(ServiceTest, ConfigureStringProperty) { |
| const std::string kGuid0 = "guid_zero"; |
| const std::string kGuid1 = "guid_one"; |
| service_->SetGuid(kGuid0, nullptr); |
| ASSERT_EQ(kGuid0, service_->guid()); |
| KeyValueStore args; |
| args.Set<std::string>(kGuidProperty, kGuid1); |
| Error error; |
| service_->Configure(args, &error); |
| EXPECT_TRUE(error.IsSuccess()); |
| EXPECT_EQ(kGuid1, service_->guid()); |
| } |
| |
| TEST_F(ServiceTest, ConfigureStringsProperty) { |
| const std::vector<std::string> kStrings0{"string0", "string1"}; |
| const std::vector<std::string> kStrings1{"string2", "string3"}; |
| service_->set_strings(kStrings0); |
| ASSERT_EQ(kStrings0, service_->strings()); |
| KeyValueStore args; |
| args.Set<Strings>(ServiceUnderTest::kStringsProperty, kStrings1); |
| Error error; |
| service_->Configure(args, &error); |
| EXPECT_TRUE(error.IsSuccess()); |
| EXPECT_EQ(kStrings1, service_->strings()); |
| } |
| |
| TEST_F(ServiceTest, ConfigureEapStringProperty) { |
| MockEapCredentials* eap = new NiceMock<MockEapCredentials>(); |
| service2_->SetEapCredentials(eap); // Passes ownership. |
| |
| const std::string kEAPManagement0 = "management_zero"; |
| const std::string kEAPManagement1 = "management_one"; |
| service2_->SetEAPKeyManagement(kEAPManagement0); |
| |
| EXPECT_CALL(*eap, key_management()).WillOnce(ReturnRef(kEAPManagement0)); |
| ASSERT_EQ(kEAPManagement0, service2_->GetEAPKeyManagement()); |
| KeyValueStore args; |
| EXPECT_CALL(*eap, SetKeyManagement(kEAPManagement1, _)); |
| args.Set<std::string>(kEapKeyMgmtProperty, kEAPManagement1); |
| Error error; |
| service2_->Configure(args, &error); |
| EXPECT_TRUE(error.IsSuccess()); |
| } |
| |
| TEST_F(ServiceTest, ConfigureIntProperty) { |
| const int kPriority0 = 100; |
| const int kPriority1 = 200; |
| service_->SetPriority(kPriority0, nullptr); |
| ASSERT_EQ(kPriority0, service_->priority()); |
| KeyValueStore args; |
| args.Set<int32_t>(kPriorityProperty, kPriority1); |
| Error error; |
| service_->Configure(args, &error); |
| EXPECT_TRUE(error.IsSuccess()); |
| EXPECT_EQ(kPriority1, service_->priority()); |
| } |
| |
| TEST_F(ServiceTest, ConfigureIgnoredProperty) { |
| service_->EnableAndRetainAutoConnect(); |
| service_->SetAutoConnect(false); |
| ASSERT_FALSE(service_->auto_connect()); |
| KeyValueStore args; |
| args.Set<bool>(kAutoConnectProperty, true); |
| Error error; |
| service_->IgnoreParameterForConfigure(kAutoConnectProperty); |
| service_->Configure(args, &error); |
| EXPECT_TRUE(error.IsSuccess()); |
| EXPECT_FALSE(service_->auto_connect()); |
| } |
| |
| TEST_F(ServiceTest, ConfigureProfileProperty) { |
| // Ensure that the Profile property is always ignored. |
| KeyValueStore args; |
| args.Set<std::string>(kProfileProperty, "profile"); |
| Error error; |
| EXPECT_CALL(mock_manager_, SetProfileForService(_, _, _)).Times(0); |
| service_->Configure(args, &error); |
| EXPECT_TRUE(error.IsSuccess()); |
| } |
| |
| TEST_F(ServiceTest, ConfigureKeyValueStoreProperty) { |
| KeyValueStore key_value_store0; |
| key_value_store0.Set<bool>("key0", true); |
| KeyValueStore key_value_store1; |
| key_value_store1.Set<int32_t>("key1", 1); |
| service_->SetKeyValueStore(key_value_store0, nullptr); |
| ASSERT_EQ(key_value_store0, service_->GetKeyValueStore(nullptr)); |
| KeyValueStore args; |
| args.Set<KeyValueStore>(ServiceUnderTest::kKeyValueStoreProperty, |
| key_value_store1); |
| Error error; |
| service_->Configure(args, &error); |
| EXPECT_TRUE(error.IsSuccess()); |
| EXPECT_EQ(key_value_store1, service_->GetKeyValueStore(nullptr)); |
| } |
| |
| TEST_F(ServiceTest, DoPropertiesMatch) { |
| service_->SetAutoConnect(false); |
| const std::string kGUID0 = "guid_zero"; |
| const std::string kGUID1 = "guid_one"; |
| service_->SetGuid(kGUID0, nullptr); |
| const uint32_t kPriority0 = 100; |
| const uint32_t kPriority1 = 200; |
| service_->SetPriority(kPriority0, nullptr); |
| const std::vector<std::string> kStrings0{"string0", "string1"}; |
| const std::vector<std::string> kStrings1{"string2", "string3"}; |
| service_->set_strings(kStrings0); |
| KeyValueStore key_value_store0; |
| key_value_store0.Set<bool>("key0", true); |
| KeyValueStore key_value_store1; |
| key_value_store1.Set<int32_t>("key1", 1); |
| service_->SetKeyValueStore(key_value_store0, nullptr); |
| |
| { |
| KeyValueStore args; |
| args.Set<std::string>(kGuidProperty, kGUID0); |
| args.Set<bool>(kAutoConnectProperty, false); |
| args.Set<int32_t>(kPriorityProperty, kPriority0); |
| args.Set<Strings>(ServiceUnderTest::kStringsProperty, kStrings0); |
| args.Set<KeyValueStore>(ServiceUnderTest::kKeyValueStoreProperty, |
| key_value_store0); |
| EXPECT_TRUE(service_->DoPropertiesMatch(args)); |
| } |
| { |
| KeyValueStore args; |
| args.Set<std::string>(kGuidProperty, kGUID1); |
| args.Set<bool>(kAutoConnectProperty, false); |
| args.Set<int32_t>(kPriorityProperty, kPriority0); |
| args.Set<Strings>(ServiceUnderTest::kStringsProperty, kStrings0); |
| args.Set<KeyValueStore>(ServiceUnderTest::kKeyValueStoreProperty, |
| key_value_store0); |
| EXPECT_FALSE(service_->DoPropertiesMatch(args)); |
| } |
| { |
| KeyValueStore args; |
| args.Set<std::string>(kGuidProperty, kGUID0); |
| args.Set<bool>(kAutoConnectProperty, true); |
| args.Set<int32_t>(kPriorityProperty, kPriority0); |
| args.Set<Strings>(ServiceUnderTest::kStringsProperty, kStrings0); |
| args.Set<KeyValueStore>(ServiceUnderTest::kKeyValueStoreProperty, |
| key_value_store0); |
| EXPECT_FALSE(service_->DoPropertiesMatch(args)); |
| } |
| { |
| KeyValueStore args; |
| args.Set<std::string>(kGuidProperty, kGUID0); |
| args.Set<bool>(kAutoConnectProperty, false); |
| args.Set<int32_t>(kPriorityProperty, kPriority1); |
| args.Set<Strings>(ServiceUnderTest::kStringsProperty, kStrings0); |
| args.Set<KeyValueStore>(ServiceUnderTest::kKeyValueStoreProperty, |
| key_value_store0); |
| EXPECT_FALSE(service_->DoPropertiesMatch(args)); |
| } |
| { |
| KeyValueStore args; |
| args.Set<std::string>(kGuidProperty, kGUID0); |
| args.Set<bool>(kAutoConnectProperty, false); |
| args.Set<int32_t>(kPriorityProperty, kPriority0); |
| args.Set<Strings>(ServiceUnderTest::kStringsProperty, kStrings1); |
| args.Set<KeyValueStore>(ServiceUnderTest::kKeyValueStoreProperty, |
| key_value_store0); |
| EXPECT_FALSE(service_->DoPropertiesMatch(args)); |
| } |
| { |
| KeyValueStore args; |
| args.Set<std::string>(kGuidProperty, kGUID0); |
| args.Set<bool>(kAutoConnectProperty, false); |
| args.Set<int32_t>(kPriorityProperty, kPriority0); |
| args.Set<Strings>(ServiceUnderTest::kStringsProperty, kStrings0); |
| args.Set<KeyValueStore>(ServiceUnderTest::kKeyValueStoreProperty, |
| key_value_store1); |
| EXPECT_FALSE(service_->DoPropertiesMatch(args)); |
| } |
| } |
| |
| TEST_F(ServiceTest, IsRemembered) { |
| service_->set_profile(nullptr); |
| EXPECT_CALL(mock_manager_, IsServiceEphemeral(_)).Times(0); |
| EXPECT_FALSE(service_->IsRemembered()); |
| |
| scoped_refptr<MockProfile> profile(new StrictMock<MockProfile>(manager())); |
| service_->set_profile(profile); |
| EXPECT_CALL(mock_manager_, IsServiceEphemeral(Pointer(service_))) |
| .WillOnce(Return(true)) |
| .WillOnce(Return(false)); |
| EXPECT_FALSE(service_->IsRemembered()); |
| EXPECT_TRUE(service_->IsRemembered()); |
| } |
| |
| TEST_F(ServiceTest, OnPropertyChanged) { |
| scoped_refptr<MockProfile> profile(new StrictMock<MockProfile>(manager())); |
| service_->set_profile(nullptr); |
| // Expect no crash. |
| service_->OnPropertyChanged(""); |
| |
| // Expect no call to Update if the profile has no storage. |
| service_->set_profile(profile); |
| EXPECT_CALL(*profile, UpdateService(_)).Times(0); |
| EXPECT_CALL(*profile, GetConstStorage()).WillOnce(Return(nullptr)); |
| service_->OnPropertyChanged(""); |
| |
| // Expect call to Update if the profile has storage. |
| EXPECT_CALL(*profile, UpdateService(_)).Times(1); |
| FakeStore storage; |
| EXPECT_CALL(*profile, GetConstStorage()).WillOnce(Return(&storage)); |
| service_->OnPropertyChanged(""); |
| } |
| |
| TEST_F(ServiceTest, SetCheckPortal) { |
| auto network = |
| std::make_unique<MockNetwork>(1, kIfName, Technology::kEthernet); |
| service_->AttachNetwork(network->AsWeakPtr()); |
| |
| // Ensure the network validation mode is set to full validation. |
| EXPECT_CALL(mock_manager_, IsPortalDetectionEnabled) |
| .WillRepeatedly(Return(true)); |
| { |
| Error error; |
| service_->SetProxyConfig("", &error); |
| } |
| SetStateField(Service::kStateConnected); |
| EXPECT_EQ(NetworkMonitor::ValidationMode::kFullValidation, |
| service_->GetNetworkValidationMode()); |
| |
| { |
| EXPECT_CALL(*network, UpdateNetworkValidationMode( |
| NetworkMonitor::ValidationMode::kDisabled)); |
| Error error; |
| service_->SetCheckPortal("false", &error); |
| EXPECT_TRUE(error.IsSuccess()); |
| EXPECT_EQ(Service::CheckPortalState::kFalse, service_->check_portal()); |
| EXPECT_EQ("false", service_->GetCheckPortal(nullptr)); |
| EXPECT_EQ(NetworkMonitor::ValidationMode::kDisabled, |
| service_->GetNetworkValidationMode()); |
| Mock::VerifyAndClearExpectations(network.get()); |
| } |
| { |
| EXPECT_CALL(*network, UpdateNetworkValidationMode( |
| NetworkMonitor::ValidationMode::kFullValidation)); |
| Error error; |
| service_->SetCheckPortal("auto", &error); |
| EXPECT_TRUE(error.IsSuccess()); |
| EXPECT_EQ(Service::CheckPortalState::kAutomatic, service_->check_portal()); |
| EXPECT_EQ("auto", service_->GetCheckPortal(nullptr)); |
| EXPECT_EQ(NetworkMonitor::ValidationMode::kFullValidation, |
| service_->GetNetworkValidationMode()); |
| Mock::VerifyAndClearExpectations(network.get()); |
| } |
| { |
| EXPECT_CALL(*network, UpdateNetworkValidationMode( |
| NetworkMonitor::ValidationMode::kFullValidation)); |
| Error error; |
| service_->SetCheckPortal("true", &error); |
| EXPECT_TRUE(error.IsSuccess()); |
| EXPECT_EQ(Service::CheckPortalState::kTrue, service_->check_portal()); |
| EXPECT_EQ("true", service_->GetCheckPortal(nullptr)); |
| EXPECT_EQ(NetworkMonitor::ValidationMode::kFullValidation, |
| service_->GetNetworkValidationMode()); |
| Mock::VerifyAndClearExpectations(network.get()); |
| } |
| { |
| EXPECT_CALL(*network, UpdateNetworkValidationMode).Times(0); |
| Error error; |
| service_->SetCheckPortal("xxx", &error); |
| EXPECT_FALSE(error.IsSuccess()); |
| EXPECT_EQ(Error::kInvalidArguments, error.type()); |
| EXPECT_EQ(Service::CheckPortalState::kTrue, service_->check_portal()); |
| EXPECT_EQ("true", service_->GetCheckPortal(nullptr)); |
| EXPECT_EQ(NetworkMonitor::ValidationMode::kFullValidation, |
| service_->GetNetworkValidationMode()); |
| Mock::VerifyAndClearExpectations(network.get()); |
| } |
| } |
| |
| TEST_F(ServiceTest, SetProxyConfig) { |
| auto network = |
| std::make_unique<MockNetwork>(1, kIfName, Technology::kEthernet); |
| service_->AttachNetwork(network->AsWeakPtr()); |
| |
| EXPECT_CALL(mock_manager_, IsPortalDetectionEnabled) |
| .WillRepeatedly(Return(true)); |
| { |
| Error error; |
| service_->SetCheckPortal("auto", &error); |
| } |
| SetStateField(Service::kStateConnected); |
| EXPECT_EQ(NetworkMonitor::ValidationMode::kFullValidation, |
| service_->GetNetworkValidationMode()); |
| |
| { |
| EXPECT_CALL(*network, UpdateNetworkValidationMode( |
| NetworkMonitor::ValidationMode::kHTTPOnly)); |
| Error error; |
| service_->SetProxyConfig("{\"mode\":\"auto_detect\"}", &error); |
| EXPECT_TRUE(error.IsSuccess()); |
| EXPECT_TRUE(service_->HasProxyConfig()); |
| EXPECT_EQ(NetworkMonitor::ValidationMode::kHTTPOnly, |
| service_->GetNetworkValidationMode()); |
| Mock::VerifyAndClearExpectations(network.get()); |
| } |
| { |
| EXPECT_CALL(*network, UpdateNetworkValidationMode( |
| NetworkMonitor::ValidationMode::kFullValidation)); |
| Error error; |
| service_->SetProxyConfig("{\"mode\":\"direct\"}", &error); |
| EXPECT_TRUE(error.IsSuccess()); |
| EXPECT_FALSE(service_->HasProxyConfig()); |
| EXPECT_EQ(NetworkMonitor::ValidationMode::kFullValidation, |
| service_->GetNetworkValidationMode()); |
| Mock::VerifyAndClearExpectations(network.get()); |
| } |
| { |
| EXPECT_CALL(*network, UpdateNetworkValidationMode( |
| NetworkMonitor::ValidationMode::kFullValidation)); |
| Error error; |
| service_->SetProxyConfig("", &error); |
| EXPECT_TRUE(error.IsSuccess()); |
| EXPECT_FALSE(service_->HasProxyConfig()); |
| EXPECT_EQ(NetworkMonitor::ValidationMode::kFullValidation, |
| service_->GetNetworkValidationMode()); |
| Mock::VerifyAndClearExpectations(network.get()); |
| } |
| { |
| // If network validation is disabled for a certain technology, no matter |
| // what the proxy config setting is, it should be disabled for this service. |
| EXPECT_CALL(mock_manager_, IsPortalDetectionEnabled) |
| .WillRepeatedly(Return(false)); |
| EXPECT_CALL(*network, UpdateNetworkValidationMode( |
| NetworkMonitor::ValidationMode::kDisabled)); |
| Error error; |
| service_->SetProxyConfig("{\"mode\":\"auto_detect\"}", &error); |
| EXPECT_TRUE(error.IsSuccess()); |
| EXPECT_TRUE(service_->HasProxyConfig()); |
| EXPECT_EQ(NetworkMonitor::ValidationMode::kDisabled, |
| service_->GetNetworkValidationMode()); |
| Mock::VerifyAndClearExpectations(network.get()); |
| } |
| } |
| |
| TEST_F(ServiceTest, NetworkValidationMode) { |
| { |
| // The service has a proxy configuration == "direct", but "CheckPortal" is |
| // "true". |
| Error error; |
| service_->SetCheckPortal("true", &error); |
| service_->SetProxyConfig("{\"mode\":\"direct\"}", &error); |
| service_->SetONCSource("None", &error); |
| EXPECT_CALL(mock_manager_, IsPortalDetectionEnabled) |
| .WillRepeatedly(Return(true)); |
| EXPECT_EQ(NetworkMonitor::ValidationMode::kFullValidation, |
| service_->GetNetworkValidationMode()); |
| } |
| { |
| // The service has a proxy configuration != "direct", but "CheckPortal" is |
| // "true". |
| Error error; |
| service_->SetCheckPortal("true", &error); |
| service_->SetProxyConfig("{\"mode\":\"auto_detect\"}", &error); |
| service_->SetONCSource("None", &error); |
| EXPECT_CALL(mock_manager_, IsPortalDetectionEnabled) |
| .WillRepeatedly(Return(true)); |
| EXPECT_EQ(NetworkMonitor::ValidationMode::kFullValidation, |
| service_->GetNetworkValidationMode()); |
| } |
| { |
| // The service is configured through ONC device policy, but "CheckPortal" is |
| // "true". |
| Error error; |
| service_->SetCheckPortal("true", &error); |
| service_->SetONCSource("DevicePolicy", &error); |
| EXPECT_CALL(mock_manager_, IsPortalDetectionEnabled) |
| .WillRepeatedly(Return(true)); |
| EXPECT_EQ(NetworkMonitor::ValidationMode::kFullValidation, |
| service_->GetNetworkValidationMode()); |
| } |
| { |
| // The service is configured through ONC user policy, but "CheckPortal" is |
| // "true". |
| Error error; |
| service_->SetCheckPortal("true", &error); |
| service_->SetProxyConfig("", &error); |
| service_->SetONCSource("UserPolicy", &error); |
| EXPECT_CALL(mock_manager_, IsPortalDetectionEnabled) |
| .WillRepeatedly(Return(true)); |
| EXPECT_EQ(NetworkMonitor::ValidationMode::kFullValidation, |
| service_->GetNetworkValidationMode()); |
| } |
| { |
| // The service's "CheckPortal" property is set to "false". |
| Error error; |
| service_->SetCheckPortal("false", &error); |
| service_->SetProxyConfig("", &error); |
| service_->SetONCSource("None", &error); |
| EXPECT_CALL(mock_manager_, IsPortalDetectionEnabled) |
| .WillRepeatedly(Return(true)); |
| EXPECT_EQ(NetworkMonitor::ValidationMode::kDisabled, |
| service_->GetNetworkValidationMode()); |
| } |
| { |
| // The service's "CheckPortal" property is set to "auto" and portal |
| // detection is disabled for this link technology. |
| Error error; |
| service_->SetCheckPortal("auto", &error); |
| service_->SetProxyConfig("", &error); |
| service_->SetONCSource("None", &error); |
| EXPECT_CALL(mock_manager_, IsPortalDetectionEnabled) |
| .WillRepeatedly(Return(false)); |
| EXPECT_EQ(NetworkMonitor::ValidationMode::kDisabled, |
| service_->GetNetworkValidationMode()); |
| } |
| { |
| // The service's "CheckPortal" property is set to "auto" and the service is |
| // configured through ONC device policy. |
| Error error; |
| service_->SetCheckPortal("auto", &error); |
| service_->SetProxyConfig("", &error); |
| service_->SetONCSource("DevicePolicy", &error); |
| EXPECT_CALL(mock_manager_, IsPortalDetectionEnabled) |
| .WillRepeatedly(Return(true)); |
| EXPECT_EQ(NetworkMonitor::ValidationMode::kDisabled, |
| service_->GetNetworkValidationMode()); |
| } |
| { |
| // The service's "CheckPortal" property is set to "auto" and the service is |
| // configured through ONC user policy. |
| Error error; |
| service_->SetCheckPortal("auto", &error); |
| service_->SetProxyConfig("", &error); |
| service_->SetONCSource("UserPolicy", &error); |
| EXPECT_CALL(mock_manager_, IsPortalDetectionEnabled) |
| .WillRepeatedly(Return(true)); |
| EXPECT_EQ(NetworkMonitor::ValidationMode::kDisabled, |
| service_->GetNetworkValidationMode()); |
| } |
| { |
| // The service's "CheckPortal" property is set to "auto" and portal |
| // detection is enabled for this link technology. |
| Error error; |
| service_->SetCheckPortal("auto", &error); |
| service_->SetProxyConfig("", &error); |
| service_->SetONCSource("None", &error); |
| EXPECT_CALL(mock_manager_, IsPortalDetectionEnabled) |
| .WillRepeatedly(Return(true)); |
| EXPECT_EQ(NetworkMonitor::ValidationMode::kFullValidation, |
| service_->GetNetworkValidationMode()); |
| } |
| { |
| // The service's "CheckPortal" property is set to "auto" and the service |
| // direct proxy configuration is ignored. |
| Error error; |
| service_->SetCheckPortal("auto", &error); |
| service_->SetProxyConfig("{\"mode\":\"direct\"}", &error); |
| service_->SetONCSource("None", &error); |
| EXPECT_CALL(mock_manager_, IsPortalDetectionEnabled) |
| .WillRepeatedly(Return(true)); |
| EXPECT_EQ(NetworkMonitor::ValidationMode::kFullValidation, |
| service_->GetNetworkValidationMode()); |
| } |
| { |
| // The service's "CheckPortal" property is set to "auto" and the service |
| // has a per-network proxy configuration. |
| Error error; |
| service_->SetCheckPortal("auto", &error); |
| service_->SetProxyConfig("{\"mode\":\"auto_detect\"}", &error); |
| service_->SetONCSource("None", &error); |
| EXPECT_CALL(mock_manager_, IsPortalDetectionEnabled) |
| .WillRepeatedly(Return(true)); |
| EXPECT_EQ(NetworkMonitor::ValidationMode::kHTTPOnly, |
| service_->GetNetworkValidationMode()); |
| } |
| { |
| // The service's "CheckPortal" property is set to "http-only" and portal |
| // detection is enabled for this link technology. |
| Error error; |
| service_->SetCheckPortal("http-only", &error); |
| service_->SetProxyConfig("", &error); |
| service_->SetONCSource("None", &error); |
| EXPECT_CALL(mock_manager_, IsPortalDetectionEnabled) |
| .WillRepeatedly(Return(true)); |
| EXPECT_EQ(NetworkMonitor::ValidationMode::kHTTPOnly, |
| service_->GetNetworkValidationMode()); |
| } |
| { |
| // The service's "CheckPortal" property is set to "http-only" and portal |
| // detection is disabled for this link technology. |
| Error error; |
| service_->SetCheckPortal("http-only", &error); |
| service_->SetProxyConfig("", &error); |
| service_->SetONCSource("None", &error); |
| EXPECT_CALL(mock_manager_, IsPortalDetectionEnabled) |
| .WillRepeatedly(Return(false)); |
| EXPECT_EQ(NetworkMonitor::ValidationMode::kHTTPOnly, |
| service_->GetNetworkValidationMode()); |
| } |
| { |
| // The service's "CheckPortal" property is set to "http-only" and the |
| // service is configured through ONC device policy. |
| Error error; |
| service_->SetCheckPortal("http-only", &error); |
| service_->SetProxyConfig("", &error); |
| service_->SetONCSource("DevicePolicy", &error); |
| EXPECT_CALL(mock_manager_, IsPortalDetectionEnabled) |
| .WillRepeatedly(Return(true)); |
| EXPECT_EQ(NetworkMonitor::ValidationMode::kHTTPOnly, |
| service_->GetNetworkValidationMode()); |
| } |
| } |
| |
| TEST_F(ServiceTest, SetFriendlyName) { |
| std::string default_friendly_name = service_->friendly_name_; |
| ServiceMockAdaptor* adaptor = GetAdaptor(); |
| |
| EXPECT_CALL(*adaptor, EmitStringChanged(_, _)).Times(0); |
| service_->SetFriendlyName(default_friendly_name); |
| EXPECT_EQ(default_friendly_name, service_->friendly_name_); |
| |
| EXPECT_CALL(*adaptor, EmitStringChanged(kNameProperty, "Test Name 1")); |
| service_->SetFriendlyName("Test Name 1"); |
| EXPECT_EQ("Test Name 1", service_->friendly_name_); |
| |
| EXPECT_CALL(*adaptor, EmitStringChanged(_, _)).Times(0); |
| service_->SetFriendlyName("Test Name 1"); |
| EXPECT_EQ("Test Name 1", service_->friendly_name_); |
| |
| EXPECT_CALL(*adaptor, EmitStringChanged(kNameProperty, "Test Name 2")); |
| service_->SetFriendlyName("Test Name 2"); |
| EXPECT_EQ("Test Name 2", service_->friendly_name_); |
| } |
| |
| TEST_F(ServiceTest, SetConnectableFull) { |
| EXPECT_TRUE(service_->connectable()); |
| |
| ServiceMockAdaptor* adaptor = GetAdaptor(); |
| |
| EXPECT_CALL(*adaptor, EmitBoolChanged(_, _)).Times(0); |
| EXPECT_CALL(mock_manager_, HasService(_)).Times(0); |
| service_->SetConnectableFull(true); |
| EXPECT_TRUE(service_->connectable()); |
| |
| EXPECT_CALL(*adaptor, EmitBoolChanged(kConnectableProperty, false)); |
| EXPECT_CALL(mock_manager_, HasService(_)).WillOnce(Return(true)); |
| EXPECT_CALL(mock_manager_, UpdateService(_)); |
| service_->SetConnectableFull(false); |
| EXPECT_FALSE(service_->connectable()); |
| |
| EXPECT_CALL(*adaptor, EmitBoolChanged(_, _)).Times(0); |
| EXPECT_CALL(mock_manager_, HasService(_)).Times(0); |
| service_->SetConnectableFull(false); |
| EXPECT_FALSE(service_->connectable()); |
| |
| EXPECT_CALL(*adaptor, EmitBoolChanged(kConnectableProperty, true)); |
| EXPECT_CALL(mock_manager_, HasService(_)).WillOnce(Return(true)); |
| EXPECT_CALL(mock_manager_, UpdateService(_)); |
| service_->SetConnectableFull(true); |
| EXPECT_TRUE(service_->connectable()); |
| } |
| |
| class WriteOnlyServicePropertyTest : public ServiceTest {}; |
| TEST_P(WriteOnlyServicePropertyTest, PropertyWriteOnly) { |
| // Use a real EapCredentials instance since the base Service class |
| // contains no write-only properties. |
| EapCredentials eap; |
| eap.InitPropertyStore(service_->mutable_store()); |
| |
| std::string property(GetParam().Get<std::string>()); |
| Error error; |
| EXPECT_FALSE(service_->store().GetStringProperty(property, nullptr, &error)); |
| EXPECT_EQ(Error::kPermissionDenied, error.type()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| WriteOnlyServicePropertyTestInstance, |
| WriteOnlyServicePropertyTest, |
| Values(brillo::Any(std::string(kEapPasswordProperty)))); |
| |
| TEST_F(ServiceTest, AttachedNetworkChangeTriggersEmitPropertyChanged) { |
| { |
| Error error; |
| EXPECT_EQ(DBusControl::NullRpcIdentifier(), |
| service_->GetIPConfigRpcIdentifier(&error)); |
| EXPECT_EQ(Error::kNotFound, error.type()); |
| EXPECT_EQ(0, service_->GetNetworkID(nullptr)); |
| } |
| |
| { |
| int fake_network_id = 335; |
| auto ipconfig = std::make_unique<IPConfig>(control_interface(), kIfName); |
| auto network = |
| std::make_unique<MockNetwork>(1, kIfName, Technology::kEthernet); |
| ON_CALL(*network, network_id).WillByDefault(Return(fake_network_id)); |
| ON_CALL(*network, GetCurrentIPConfig()) |
| .WillByDefault(Return(ipconfig.get())); |
| |
| EXPECT_CALL(*GetAdaptor(), |
| EmitIntChanged(kNetworkIDProperty, fake_network_id)); |
| EXPECT_CALL(*GetAdaptor(), |
| EmitRpcIdentifierChanged(kIPConfigProperty, |
| ipconfig->GetRpcIdentifier())); |
| service_->AttachNetwork(network->AsWeakPtr()); |
| EXPECT_EQ(fake_network_id, service_->GetNetworkID(nullptr)); |
| Error error; |
| EXPECT_EQ(ipconfig->GetRpcIdentifier(), |
| service_->GetIPConfigRpcIdentifier(&error)); |
| EXPECT_TRUE(error.IsSuccess()); |
| |
| EXPECT_CALL(*GetAdaptor(), EmitIntChanged(kNetworkIDProperty, 0)); |
| EXPECT_CALL(*GetAdaptor(), EmitRpcIdentifierChanged(kIPConfigProperty, _)) |
| .Times(0); |
| service_->DetachNetwork(); |
| } |
| |
| { |
| Error error; |
| EXPECT_EQ(DBusControl::NullRpcIdentifier(), |
| service_->GetIPConfigRpcIdentifier(&error)); |
| EXPECT_EQ(Error::kNotFound, error.type()); |
| EXPECT_EQ(0, service_->GetNetworkID(nullptr)); |
| } |
| |
| { |
| int fake_network_id = 744; |
| auto network = |
| std::make_unique<MockNetwork>(1, kIfName, Technology::kEthernet); |
| ON_CALL(*network, network_id).WillByDefault(Return(fake_network_id)); |
| EXPECT_CALL(*GetAdaptor(), |
| EmitIntChanged(kNetworkIDProperty, fake_network_id)); |
| EXPECT_CALL(*GetAdaptor(), EmitRpcIdentifierChanged(kIPConfigProperty, _)) |
| .Times(0); |
| service_->AttachNetwork(network->AsWeakPtr()); |
| EXPECT_EQ(fake_network_id, service_->GetNetworkID(nullptr)); |
| |
| Error error; |
| EXPECT_EQ(DBusControl::NullRpcIdentifier(), |
| service_->GetIPConfigRpcIdentifier(&error)); |
| EXPECT_EQ(Error::kNotFound, error.type()); |
| } |
| } |
| |
| class ServiceWithMockOnEapCredentialsChanged : public ServiceUnderTest { |
| public: |
| explicit ServiceWithMockOnEapCredentialsChanged(Manager* manager) |
| : ServiceUnderTest(manager) {} |
| MOCK_METHOD(void, |
| OnEapCredentialsChanged, |
| (Service::UpdateCredentialsReason), |
| (override)); |
| bool Is8021x() const override { return is_8021x_; } |
| void set_is_8021x(bool is_8021x) { is_8021x_ = is_8021x; } |
| |
| private: |
| bool is_8021x_ = false; |
| }; |
| |
| TEST_F(ServiceTest, SetEAPCredentialsOverRpc) { |
| scoped_refptr<ServiceWithMockOnEapCredentialsChanged> service( |
| new ServiceWithMockOnEapCredentialsChanged(&mock_manager_)); |
| static const char* const kEapCredentialProperties[] = { |
| kEapAnonymousIdentityProperty, kEapCertIdProperty, |
| kEapIdentityProperty, kEapKeyIdProperty, |
| kEapPasswordProperty, kEapPinProperty, |
| }; |
| static const char* const kEapNonCredentialProperties[] = { |
| kEapCaCertIdProperty, kEapMethodProperty, kEapPhase2AuthProperty, |
| kEapUseSystemCasProperty}; |
| // While this is not an 802.1x-based service, none of these property |
| // changes should cause a call to set_eap(). |
| EXPECT_CALL(*service, OnEapCredentialsChanged(_)).Times(0); |
| for (auto eap_credential_property : kEapCredentialProperties) { |
| service->OnPropertyChanged(eap_credential_property); |
| } |
| for (auto eap_non_credential_property : kEapNonCredentialProperties) { |
| service->OnPropertyChanged(eap_non_credential_property); |
| } |
| service->OnPropertyChanged(kEapKeyMgmtProperty); |
| |
| service->set_is_8021x(true); |
| |
| // When this is an 802.1x-based service, set_eap should be called for |
| // all credential-carrying properties. |
| for (auto eap_credential_property : kEapCredentialProperties) { |
| EXPECT_CALL(*service, |
| OnEapCredentialsChanged(Service::kReasonPropertyUpdate)) |
| .Times(1); |
| service->OnPropertyChanged(eap_credential_property); |
| Mock::VerifyAndClearExpectations(service.get()); |
| } |
| |
| // The key management property is a special case. While not strictly |
| // a credential, it can change which credentials are used. Therefore it |
| // should also trigger a call to set_eap(); |
| EXPECT_CALL(*service, OnEapCredentialsChanged(Service::kReasonPropertyUpdate)) |
| .Times(1); |
| service->OnPropertyChanged(kEapKeyMgmtProperty); |
| Mock::VerifyAndClearExpectations(service.get()); |
| |
| EXPECT_CALL(*service, OnEapCredentialsChanged(_)).Times(0); |
| for (auto eap_non_credential_property : kEapNonCredentialProperties) { |
| service->OnPropertyChanged(eap_non_credential_property); |
| } |
| } |
| |
| TEST_F(ServiceTest, Certification) { |
| EXPECT_TRUE(service_->remote_certification_.empty()); |
| |
| ScopedMockLog log; |
| EXPECT_CALL( |
| log, Log(logging::LOGGING_WARNING, _, HasSubstr("exceeds our maximum"))) |
| .Times(2); |
| std::string kSubject("foo"); |
| EXPECT_FALSE(service_->AddEAPCertification( |
| kSubject, Service::kEAPMaxCertificationElements)); |
| EXPECT_FALSE(service_->AddEAPCertification( |
| kSubject, Service::kEAPMaxCertificationElements + 1)); |
| EXPECT_FALSE(service_->remote_certification_.size()); |
| Mock::VerifyAndClearExpectations(&log); |
| |
| EXPECT_CALL( |
| log, Log(logging::LOGGING_INFO, _, HasSubstr("Received certification"))) |
| .Times(1); |
| EXPECT_TRUE(service_->AddEAPCertification( |
| kSubject, Service::kEAPMaxCertificationElements - 1)); |
| Mock::VerifyAndClearExpectations(&log); |
| EXPECT_EQ(Service::kEAPMaxCertificationElements, |
| service_->remote_certification_.size()); |
| for (size_t i = 0; i < Service::kEAPMaxCertificationElements - 1; ++i) { |
| EXPECT_TRUE(service_->remote_certification_[i].empty()); |
| } |
| EXPECT_EQ( |
| kSubject, |
| service_ |
| ->remote_certification_[Service::kEAPMaxCertificationElements - 1]); |
| |
| // Re-adding the same name in the same position should not generate a log. |
| EXPECT_CALL(log, Log(_, _, _)).Times(0); |
| EXPECT_TRUE(service_->AddEAPCertification( |
| kSubject, Service::kEAPMaxCertificationElements - 1)); |
| |
| // Replacing the item should generate a log message. |
| EXPECT_CALL( |
| log, Log(logging::LOGGING_INFO, _, HasSubstr("Received certification"))) |
| .Times(1); |
| EXPECT_TRUE(service_->AddEAPCertification( |
| kSubject + "x", Service::kEAPMaxCertificationElements - 1)); |
| |
| service_->ClearEAPCertification(); |
| EXPECT_TRUE(service_->remote_certification_.empty()); |
| } |
| |
| TEST_F(ServiceTest, NoteFailureEventIdle) { |
| Timestamp timestamp; |
| EXPECT_CALL(time_, GetNow()).Times(7).WillRepeatedly((Return(timestamp))); |
| SetStateField(Service::kStateOnline); |
| EXPECT_FALSE(service_->HasRecentConnectionIssues()); |
| service_->SetState(Service::kStateIdle); |
| // The transition Online->Idle is not an event. |
| EXPECT_FALSE(service_->HasRecentConnectionIssues()); |
| service_->SetState(Service::kStateFailure); |
| // The transition Online->Idle->Failure is a connection drop. |
| EXPECT_TRUE(service_->HasRecentConnectionIssues()); |
| } |
| |
| TEST_F(ServiceTest, NoteFailureEventOnSetStateFailure) { |
| Timestamp timestamp; |
| EXPECT_CALL(time_, GetNow()).Times(5).WillRepeatedly((Return(timestamp))); |
| SetStateField(Service::kStateOnline); |
| EXPECT_FALSE(service_->HasRecentConnectionIssues()); |
| service_->SetState(Service::kStateFailure); |
| EXPECT_TRUE(service_->HasRecentConnectionIssues()); |
| } |
| |
| TEST_F(ServiceTest, NoteFailureEventOnSetFailureSilent) { |
| Timestamp timestamp; |
| EXPECT_CALL(time_, GetNow()).Times(5).WillRepeatedly((Return(timestamp))); |
| SetStateField(Service::kStateConfiguring); |
| EXPECT_FALSE(service_->HasRecentConnectionIssues()); |
| service_->SetFailureSilent(Service::kFailureEAPAuthentication); |
| EXPECT_TRUE(service_->HasRecentConnectionIssues()); |
| } |
| |
| TEST_F(ServiceTest, NoteFailureEventNonEvent) { |
| EXPECT_CALL(time_, GetNow()).Times(0); |
| |
| // Explicit disconnect is a non-event. |
| SetStateField(Service::kStateOnline); |
| SetExplicitlyDisconnected(true); |
| NoteFailureEvent(); |
| EXPECT_TRUE(GetDisconnects()->Empty()); |
| EXPECT_TRUE(GetMisconnects()->Empty()); |
| |
| // Failure to idle transition is a non-event. |
| SetStateField(Service::kStateFailure); |
| SetExplicitlyDisconnected(false); |
| NoteFailureEvent(); |
| EXPECT_TRUE(GetDisconnects()->Empty()); |
| EXPECT_TRUE(GetMisconnects()->Empty()); |
| |
| // Disconnect while manager is stopped is a non-event. |
| SetStateField(Service::kStateOnline); |
| SetManagerRunning(false); |
| NoteFailureEvent(); |
| EXPECT_TRUE(GetDisconnects()->Empty()); |
| EXPECT_TRUE(GetMisconnects()->Empty()); |
| |
| // Disconnect while suspending is a non-event. |
| SetManagerRunning(true); |
| SetSuspending(true); |
| NoteFailureEvent(); |
| EXPECT_TRUE(GetDisconnects()->Empty()); |
| EXPECT_TRUE(GetMisconnects()->Empty()); |
| } |
| |
| TEST_F(ServiceTest, NoteFailureEventDisconnectOnce) { |
| const int kNow = 5; |
| EXPECT_FALSE(service_->explicitly_disconnected()); |
| SetStateField(Service::kStateOnline); |
| EXPECT_CALL(time_, GetNow()).WillOnce(Return(GetTimestamp(kNow, kNow, ""))); |
| NoteFailureEvent(); |
| ASSERT_EQ(1, GetDisconnects()->Size()); |
| EXPECT_EQ(kNow, GetDisconnects()->Front().monotonic.tv_sec); |
| EXPECT_TRUE(GetMisconnects()->Empty()); |
| |
| Mock::VerifyAndClearExpectations(&time_); |
| EXPECT_CALL(time_, GetNow()) |
| .Times(2) |
| .WillRepeatedly( |
| Return(GetTimestamp(kNow + GetDisconnectsMonitorSeconds() - 1, |
| kNow + GetDisconnectsMonitorSeconds() - 1, ""))); |
| EXPECT_TRUE(service_->HasRecentConnectionIssues()); |
| ASSERT_EQ(1, GetDisconnects()->Size()); |
| |
| Mock::VerifyAndClearExpectations(&time_); |
| EXPECT_CALL(time_, GetNow()) |
| .Times(2) |
| .WillRepeatedly( |
| Return(GetTimestamp(kNow + GetDisconnectsMonitorSeconds(), |
| kNow + GetDisconnectsMonitorSeconds(), ""))); |
| EXPECT_FALSE(service_->HasRecentConnectionIssues()); |
| ASSERT_TRUE(GetDisconnects()->Empty()); |
| } |
| |
| TEST_F(ServiceTest, NoteFailureEventMisconnectOnce) { |
| const int kNow = 7; |
| EXPECT_FALSE(service_->explicitly_disconnected()); |
| SetStateField(Service::kStateConfiguring); |
| EXPECT_CALL(time_, GetNow()).WillOnce(Return(GetTimestamp(kNow, kNow, ""))); |
| NoteFailureEvent(); |
| EXPECT_TRUE(GetDisconnects()->Empty()); |
| ASSERT_EQ(1, GetMisconnects()->Size()); |
| EXPECT_EQ(kNow, GetMisconnects()->Front().monotonic.tv_sec); |
| |
| Mock::VerifyAndClearExpectations(&time_); |
| EXPECT_CALL(time_, GetNow()) |
| .Times(2) |
| .WillRepeatedly( |
| Return(GetTimestamp(kNow + GetMisconnectsMonitorSeconds() - 1, |
| kNow + GetMisconnectsMonitorSeconds() - 1, ""))); |
| EXPECT_TRUE(service_->HasRecentConnectionIssues()); |
| ASSERT_EQ(1, GetMisconnects()->Size()); |
| |
| Mock::VerifyAndClearExpectations(&time_); |
| EXPECT_CALL(time_, GetNow()) |
| .Times(2) |
| .WillRepeatedly( |
| Return(GetTimestamp(kNow + GetMisconnectsMonitorSeconds(), |
| kNow + GetMisconnectsMonitorSeconds(), ""))); |
| EXPECT_FALSE(service_->HasRecentConnectionIssues()); |
| ASSERT_TRUE(GetMisconnects()->Empty()); |
| } |
| |
| TEST_F(ServiceTest, NoteFailureEventDiscardOld) { |
| EXPECT_FALSE(service_->explicitly_disconnected()); |
| for (int i = 0; i < 2; i++) { |
| int now = 0; |
| EventHistory* events = nullptr; |
| if (i == 0) { |
| SetStateField(Service::kStateConnected); |
| now = GetDisconnectsMonitorSeconds() + 1; |
| events = GetDisconnects(); |
| } else { |
| SetStateField(Service::kStateAssociating); |
| now = GetMisconnectsMonitorSeconds() + 1; |
| events = GetMisconnects(); |
| } |
| PushTimestamp(events, 0, 0, ""); |
| PushTimestamp(events, 0, 0, ""); |
| EXPECT_CALL(time_, GetNow()).WillOnce(Return(GetTimestamp(now, now, ""))); |
| NoteFailureEvent(); |
| ASSERT_EQ(1, events->Size()); |
| EXPECT_EQ(now, events->Front().monotonic.tv_sec); |
| } |
| } |
| |
| TEST_F(ServiceTest, NoteFailureEventDiscardExcessive) { |
| EXPECT_FALSE(service_->explicitly_disconnected()); |
| SetStateField(Service::kStateOnline); |
| for (int i = 0; i < 2 * GetMaxDisconnectEventHistory(); i++) { |
| PushTimestamp(GetDisconnects(), 0, 0, ""); |
| } |
| EXPECT_CALL(time_, GetNow()).WillOnce(Return(Timestamp())); |
| NoteFailureEvent(); |
| EXPECT_EQ(GetMaxDisconnectEventHistory(), GetDisconnects()->Size()); |
| } |
| |
| TEST_F(ServiceTest, NoteMisconnectEventDiscardExcessive) { |
| EXPECT_FALSE(service_->explicitly_disconnected()); |
| SetStateField(Service::kStateAssociating); |
| for (int i = 0; i < 2 * GetMaxMisconnectEventHistory(); i++) { |
| PushTimestamp(GetMisconnects(), 0, 0, ""); |
| } |
| EXPECT_CALL(time_, GetNow()).WillOnce(Return(Timestamp())); |
| NoteFailureEvent(); |
| EXPECT_EQ(GetMaxMisconnectEventHistory(), GetMisconnects()->Size()); |
| } |
| |
| TEST_F(ServiceTest, DiagnosticsProperties) { |
| const std::string kWallClock0 = "2012-12-09T12:41:22.234567-0800"; |
| const std::string kWallClock1 = "2012-12-31T23:59:59.345678-0800"; |
| Strings values; |
| |
| PushTimestamp(GetDisconnects(), 0, 0, kWallClock0); |
| Error unused_error; |
| ASSERT_TRUE(service_->store().GetStringsProperty( |
| kDiagnosticsDisconnectsProperty, &values, &unused_error)); |
| ASSERT_EQ(1, values.size()); |
| EXPECT_EQ(kWallClock0, values[0]); |
| |
| PushTimestamp(GetMisconnects(), 0, 0, kWallClock1); |
| ASSERT_TRUE(service_->store().GetStringsProperty( |
| kDiagnosticsMisconnectsProperty, &values, &unused_error)); |
| ASSERT_EQ(1, values.size()); |
| EXPECT_EQ(kWallClock1, values[0]); |
| } |
| |
| TEST_F(ServiceTest, SecurityLevel) { |
| // Encrypted is better than not. |
| service_->SetSecurity(Service::kCryptoNone, false, false); |
| service2_->SetSecurity(Service::kCryptoRc4, false, false); |
| EXPECT_GT(service2_->SecurityLevel(), service_->SecurityLevel()); |
| |
| // AES encryption is better than RC4 encryption. |
| service_->SetSecurity(Service::kCryptoRc4, false, false); |
| service2_->SetSecurity(Service::kCryptoAes, false, false); |
| EXPECT_GT(service2_->SecurityLevel(), service_->SecurityLevel()); |
| |
| // Crypto algorithm is more important than key rotation. |
| service_->SetSecurity(Service::kCryptoNone, true, false); |
| service2_->SetSecurity(Service::kCryptoAes, false, false); |
| EXPECT_GT(service2_->SecurityLevel(), service_->SecurityLevel()); |
| |
| // Encrypted-but-unauthenticated is better than clear-but-authenticated. |
| service_->SetSecurity(Service::kCryptoNone, false, true); |
| service2_->SetSecurity(Service::kCryptoAes, false, false); |
| EXPECT_GT(service2_->SecurityLevel(), service_->SecurityLevel()); |
| |
| // For same encryption, prefer key rotation. |
| service_->SetSecurity(Service::kCryptoRc4, false, false); |
| service2_->SetSecurity(Service::kCryptoRc4, true, false); |
| EXPECT_GT(service2_->SecurityLevel(), service_->SecurityLevel()); |
| |
| // For same encryption, prefer authenticated AP. |
| service_->SetSecurity(Service::kCryptoRc4, false, false); |
| service2_->SetSecurity(Service::kCryptoRc4, false, true); |
| EXPECT_GT(service2_->SecurityLevel(), service_->SecurityLevel()); |
| } |
| |
| TEST_F(ServiceTest, SetErrorDetails) { |
| EXPECT_EQ(Service::kErrorDetailsNone, service_->error_details()); |
| static const char kDetails[] = "Certificate revoked."; |
| ServiceMockAdaptor* adaptor = GetAdaptor(); |
| EXPECT_CALL(*adaptor, EmitStringChanged(kErrorDetailsProperty, kDetails)); |
| service_->SetErrorDetails(Service::kErrorDetailsNone); |
| EXPECT_EQ(Service::kErrorDetailsNone, service_->error_details()); |
| service_->SetErrorDetails(kDetails); |
| EXPECT_EQ(kDetails, service_->error_details()); |
| service_->SetErrorDetails(kDetails); |
| } |
| |
| TEST_F(ServiceTest, SetAutoConnectFull) { |
| EXPECT_FALSE(service_->auto_connect()); |
| Error error; |
| EXPECT_FALSE(GetAutoConnect(&error)); |
| EXPECT_TRUE(error.IsSuccess()); |
| |
| // false -> false |
| EXPECT_FALSE(service_->retain_auto_connect()); |
| EXPECT_CALL(mock_manager_, UpdateService(_)).Times(0); |
| SetAutoConnectFull(false, &error); |
| EXPECT_TRUE(error.IsSuccess()); |
| EXPECT_FALSE(service_->auto_connect()); |
| EXPECT_TRUE(service_->retain_auto_connect()); |
| EXPECT_FALSE(GetAutoConnect(nullptr)); |
| Mock::VerifyAndClearExpectations(&mock_manager_); |
| |
| // Clear the |retain_auto_connect_| flag for the next test. |
| service_->Unload(); |
| ASSERT_FALSE(service_->retain_auto_connect()); |
| |
| // false -> true |
| EXPECT_CALL(mock_manager_, UpdateService(_)).Times(1); |
| SetAutoConnectFull(true, &error); |
| EXPECT_TRUE(error.IsSuccess()); |
| EXPECT_TRUE(service_->auto_connect()); |
| EXPECT_TRUE(GetAutoConnect(nullptr)); |
| EXPECT_TRUE(service_->retain_auto_connect()); |
| Mock::VerifyAndClearExpectations(&mock_manager_); |
| |
| // Clear the |retain_auto_connect_| flag for the next test. |
| service_->Unload(); |
| ASSERT_FALSE(service_->retain_auto_connect()); |
| |
| // true -> true |
| service_->SetAutoConnect(true); |
| EXPECT_CALL(mock_manager_, UpdateService(_)).Times(0); |
| SetAutoConnectFull(true, &error); |
| EXPECT_TRUE(error.IsSuccess()); |
| EXPECT_TRUE(service_->auto_connect()); |
| EXPECT_TRUE(GetAutoConnect(nullptr)); |
| EXPECT_TRUE(service_->retain_auto_connect()); |
| Mock::VerifyAndClearExpectations(&mock_manager_); |
| |
| // Clear the |retain_auto_connect_| flag for the next test. |
| service_->Unload(); |
| ASSERT_FALSE(service_->retain_auto_connect()); |
| |
| // true -> false |
| service_->SetAutoConnect(true); |
| EXPECT_CALL(mock_manager_, UpdateService(_)).Times(1); |
| SetAutoConnectFull(false, &error); |
| EXPECT_TRUE(error.IsSuccess()); |
| EXPECT_FALSE(service_->auto_connect()); |
| EXPECT_FALSE(GetAutoConnect(nullptr)); |
| EXPECT_TRUE(service_->retain_auto_connect()); |
| Mock::VerifyAndClearExpectations(&mock_manager_); |
| } |
| |
| TEST_F(ServiceTest, SetAutoConnectFullUserUpdatePersists) { |
| // If the user sets the kAutoConnectProperty explicitly, the preference must |
| // be persisted, even if the property was not changed. |
| Error error; |
| MockProfileRefPtr mock_profile(new MockProfile(&mock_manager_)); |
| FakeStore storage; |
| service_->set_profile(mock_profile); |
| service_->SetAutoConnect(true); |
| |
| EXPECT_CALL(*mock_profile, UpdateService(_)); |
| EXPECT_CALL(*mock_profile, GetConstStorage()).WillOnce(Return(&storage)); |
| EXPECT_CALL(mock_manager_, IsServiceEphemeral(Pointer(service_))) |
| .WillOnce(Return(false)); |
| EXPECT_FALSE(service_->retain_auto_connect()); |
| SetAutoConnectFull(true, &error); |
| EXPECT_TRUE(error.IsSuccess()); |
| EXPECT_TRUE(service_->auto_connect()); |
| EXPECT_TRUE(service_->retain_auto_connect()); |
| } |
| |
| TEST_F(ServiceTest, ClearAutoConnect) { |
| EXPECT_FALSE(service_->auto_connect()); |
| Error error; |
| EXPECT_FALSE(GetAutoConnect(&error)); |
| EXPECT_TRUE(error.IsSuccess()); |
| |
| // unset -> false |
| EXPECT_FALSE(service_->retain_auto_connect()); |
| EXPECT_CALL(mock_manager_, UpdateService(_)).Times(0); |
| ClearAutoConnect(&error); |
| EXPECT_TRUE(error.IsSuccess()); |
| EXPECT_FALSE(service_->retain_auto_connect()); |
| EXPECT_FALSE(GetAutoConnect(nullptr)); |
| Mock::VerifyAndClearExpectations(&mock_manager_); |
| |
| // false -> false |
| SetAutoConnectFull(false, &error); |
| EXPECT_FALSE(GetAutoConnect(nullptr)); |
| EXPECT_TRUE(service_->retain_auto_connect()); |
| EXPECT_CALL(mock_manager_, UpdateService(_)).Times(0); |
| ClearAutoConnect(&error); |
| EXPECT_TRUE(error.IsSuccess()); |
| EXPECT_FALSE(service_->retain_auto_connect()); |
| EXPECT_FALSE(GetAutoConnect(nullptr)); |
| Mock::VerifyAndClearExpectations(&mock_manager_); |
| |
| // true -> false |
| SetAutoConnectFull(true, &error); |
| EXPECT_TRUE(error.IsSuccess()); |
| EXPECT_TRUE(GetAutoConnect(nullptr)); |
| EXPECT_CALL(mock_manager_, UpdateService(_)).Times(1); |
| ClearAutoConnect(&error); |
| EXPECT_FALSE(service_->retain_auto_connect()); |
| EXPECT_FALSE(GetAutoConnect(nullptr)); |
| Mock::VerifyAndClearExpectations(&mock_manager_); |
| } |
| |
| TEST_F(ServiceTest, UniqueAttributes) { |
| EXPECT_NE(service_->serial_number_, service2_->serial_number_); |
| } |
| |
| TEST_F(ServiceTest, PropertyChanges) { |
| TestCommonPropertyChanges(service_, GetAdaptor()); |
| TestAutoConnectPropertyChange(service_, GetAdaptor()); |
| } |
| |
| // Custom property setters should return false, and make no changes, if |
| // the new value is the same as the old value. |
| TEST_F(ServiceTest, CustomSetterNoopChange) { |
| TestCustomSetterNoopChange(service_, &mock_manager_); |
| } |
| |
| TEST_F(ServiceTest, GetTethering) { |
| EXPECT_EQ(Service::TetheringState::kUnknown, service_->GetTethering()); |
| } |
| |
| TEST_F(ServiceTest, MeteredOverride) { |
| Error error; |
| service_->SetMeteredProperty(true, &error); |
| EXPECT_TRUE(service_->IsMetered()); |
| |
| service_->SetMeteredProperty(false, &error); |
| EXPECT_FALSE(service_->IsMetered()); |
| } |
| |
| TEST_F(ServiceTest, SaveMeteredOverride) { |
| FakeStore storage; |
| EXPECT_TRUE(service_->Save(&storage)); |
| |
| // Newly created services should not have a metered override value |
| // since that is set by the user, and should thus have no value |
| // to save to the storage. |
| bool metered_override = false; |
| EXPECT_FALSE(storage.GetBool(storage_id_, Service::kStorageMeteredOverride, |
| &metered_override)); |
| |
| Error error; |
| service_->SetMeteredProperty(true, &error); |
| EXPECT_TRUE(service_->Save(&storage)); |
| EXPECT_TRUE(storage.GetBool(storage_id_, Service::kStorageMeteredOverride, |
| &metered_override)); |
| EXPECT_TRUE(metered_override); |
| } |
| |
| TEST_F(ServiceTest, IsNotMeteredByDefault) { |
| EXPECT_FALSE(service_->IsMetered()); |
| } |
| |
| class ServiceWithMockOnPropertyChanged : public ServiceUnderTest { |
| public: |
| explicit ServiceWithMockOnPropertyChanged(Manager* manager) |
| : ServiceUnderTest(manager) {} |
| MOCK_METHOD(void, OnPropertyChanged, (std::string_view), (override)); |
| }; |
| |
| TEST_F(ServiceTest, ConfigureServiceTriggersOnPropertyChanged) { |
| auto service( |
| base::MakeRefCounted<ServiceWithMockOnPropertyChanged>(&mock_manager_)); |
| KeyValueStore args; |
| args.Set<std::string>(kUIDataProperty, "terpsichorean ejectamenta"); |
| args.Set<bool>(kSaveCredentialsProperty, false); |
| |
| // Calling Configure with different values from before triggers a single |
| // OnPropertyChanged call per property. |
| EXPECT_CALL(*service, OnPropertyChanged(Eq(kUIDataProperty))).Times(1); |
| EXPECT_CALL(*service, OnPropertyChanged(Eq(kSaveCredentialsProperty))) |
| .Times(1); |
| { |
| Error error; |
| service->Configure(args, &error); |
| EXPECT_TRUE(error.IsSuccess()); |
| } |
| Mock::VerifyAndClearExpectations(service.get()); |
| |
| // Calling Configure with the same values as before should not trigger |
| // OnPropertyChanged(). |
| EXPECT_CALL(*service, OnPropertyChanged(_)).Times(0); |
| { |
| Error error; |
| service->Configure(args, &error); |
| EXPECT_TRUE(error.IsSuccess()); |
| } |
| } |
| |
| TEST_F(ServiceTest, ClearExplicitlyDisconnected) { |
| EXPECT_FALSE(GetExplicitlyDisconnected()); |
| EXPECT_CALL(mock_manager_, UpdateService(_)).Times(0); |
| service_->ClearExplicitlyDisconnected(); |
| Mock::VerifyAndClearExpectations(&mock_manager_); |
| |
| SetExplicitlyDisconnected(true); |
| EXPECT_CALL(mock_manager_, UpdateService(Pointer(service_))); |
| service_->ClearExplicitlyDisconnected(); |
| Mock::VerifyAndClearExpectations(&mock_manager_); |
| EXPECT_FALSE(GetExplicitlyDisconnected()); |
| } |
| |
| TEST_F(ServiceTest, Compare) { |
| std::vector<scoped_refptr<MockService>> mock_services; |
| for (size_t i = 0; i < 11; ++i) { |
| mock_services.push_back(new NiceMock<MockService>(&mock_manager_)); |
| } |
| scoped_refptr<MockService> service2 = mock_services[2]; |
| scoped_refptr<MockService> service10 = mock_services[10]; |
| mock_services.clear(); |
| |
| // Services should already be sorted by |serial_number_|. |
| EXPECT_TRUE(DefaultSortingOrderIs(service2, service10)); |
| |
| // Two otherwise equal services should be reordered by strength |
| service10->SetStrength(1); |
| EXPECT_TRUE(DefaultSortingOrderIs(service10, service2)); |
| |
| // A service that has been connected before should be considered |
| // above a service that has never been connected to before. |
| service2->has_ever_connected_ = true; |
| EXPECT_TRUE(DefaultSortingOrderIs(service2, service10)); |
| |
| scoped_refptr<MockProfile> profile2(new MockProfile(manager(), "")); |
| scoped_refptr<MockProfile> profile10(new MockProfile(manager(), "")); |
| |
| service2->set_profile(profile2); |
| service10->set_profile(profile10); |
| |
| // When comparing two services with different profiles, prefer the one |
| // that is not ephemeral. |
| EXPECT_CALL(mock_manager_, IsServiceEphemeral(Pointer(service2))) |
| .WillRepeatedly(Return(true)); |
| EXPECT_CALL(mock_manager_, IsServiceEphemeral(Pointer(service10))) |
| .WillRepeatedly(Return(false)); |
| EXPECT_TRUE(DefaultSortingOrderIs(service10, service2)); |
| Mock::VerifyAndClearExpectations(&mock_manager_); |
| |
| // Prefer the service with the more recently applied profile if neither |
| // service is ephemeral. |
| EXPECT_CALL(mock_manager_, IsServiceEphemeral(_)) |
| .WillRepeatedly(Return(false)); |
| EXPECT_CALL(mock_manager_, |
| IsProfileBefore(Pointer(profile2), Pointer(profile10))) |
| .WillRepeatedly(Return(false)); |
| EXPECT_CALL(mock_manager_, |
| IsProfileBefore(Pointer(profile10), Pointer(profile2))) |
| .WillRepeatedly(Return(true)); |
| EXPECT_TRUE(DefaultSortingOrderIs(service2, service10)); |
| |
| // Security. |
| service10->SetSecurity(Service::kCryptoAes, true, true); |
| EXPECT_TRUE(DefaultSortingOrderIs(service10, service2)); |
| |
| // Auto-connect. |
| service2->SetAutoConnect(true); |
| EXPECT_TRUE(DefaultSortingOrderIs(service2, service10)); |
| |
| // Managed credentials |
| service10->managed_credentials_ = true; |
| EXPECT_TRUE(DefaultSortingOrderIs(service10, service2)); |
| |
| // Priority. |
| service2->SetPriority(1, nullptr); |
| EXPECT_TRUE(DefaultSortingOrderIs(service2, service10)); |
| service10->SetPriority(2, nullptr); |
| EXPECT_TRUE(DefaultSortingOrderIs(service10, service2)); |
| |
| // Technology. |
| EXPECT_CALL(*service2, technology()) |
| .WillRepeatedly(Return(Technology::kEthernet)); |
| EXPECT_CALL(*service10, technology()) |
| .WillRepeatedly(Return((Technology::kWiFi))); |
| |
| technology_order_for_sorting_ = {Technology::kEthernet, Technology::kWiFi}; |
| EXPECT_TRUE(DefaultSortingOrderIs(service2, service10)); |
| |
| // Connectable. |
| service10->SetConnectable(true); |
| service2->SetConnectable(false); |
| EXPECT_TRUE(DefaultSortingOrderIs(service10, service2)); |
| |
| // IsFailed. |
| EXPECT_CALL(*service2, state()).WillRepeatedly(Return(Service::kStateIdle)); |
| EXPECT_CALL(*service2, IsFailed()).WillRepeatedly(Return(false)); |
| EXPECT_CALL(*service10, state()) |
| .WillRepeatedly(Return(Service::kStateFailure)); |
| EXPECT_CALL(*service10, IsFailed()).WillRepeatedly(Return(true)); |
| EXPECT_TRUE(DefaultSortingOrderIs(service2, service10)); |
| |
| // Connecting. |
| EXPECT_CALL(*service10, state()) |
| .WillRepeatedly(Return(Service::kStateAssociating)); |
| EXPECT_CALL(*service10, IsConnecting()).WillRepeatedly(Return(true)); |
| EXPECT_TRUE(DefaultSortingOrderIs(service10, service2)); |
| |
| // Connected-but-portalled preferred over unconnected. |
| EXPECT_CALL(*service2, state()) |
| .WillRepeatedly(Return(Service::kStateNoConnectivity)); |
| EXPECT_CALL(*service2, IsConnected(nullptr)).WillRepeatedly(Return(true)); |
| EXPECT_TRUE(DefaultSortingOrderIs(service2, service10)); |
| |
| // Connected preferred over connected-but-portalled. |
| service10->SetConnectable(false); |
| service2->SetConnectable(true); |
| EXPECT_CALL(*service10, state()) |
| .WillRepeatedly(Return(Service::kStateConnected)); |
| EXPECT_CALL(*service10, IsConnected(nullptr)).WillRepeatedly(Return(true)); |
| EXPECT_TRUE(DefaultSortingOrderIs(service10, service2)); |
| |
| // Online preferred over just connected. |
| EXPECT_CALL(*service2, state()).WillRepeatedly(Return(Service::kStateOnline)); |
| EXPECT_TRUE(DefaultSortingOrderIs(service10, service2)); |
| |
| // Connectivity state ignored if this is specified. |
| const bool kDoNotCompareConnectivityState = false; |
| EXPECT_TRUE( |
| SortingOrderIs(service2, service10, kDoNotCompareConnectivityState)); |
| } |
| |
| TEST_F(ServiceTest, ComparePreferEthernetOverWifi) { |
| // Create mock ethernet service. |
| scoped_refptr<MockService> ethernet_service( |
| new NiceMock<MockService>(manager())); |
| EXPECT_CALL(*ethernet_service.get(), technology()) |
| .WillRepeatedly(Return(Technology::kEthernet)); |
| |
| // Create mock wifi service. |
| scoped_refptr<MockService> wifi_service(new NiceMock<MockService>(manager())); |
| EXPECT_CALL(*wifi_service.get(), technology()) |
| .WillRepeatedly(Return((Technology::kWiFi))); |
| |
| // Confirm that ethernet service is sorted above wifi service. |
| technology_order_for_sorting_ = {Technology::kEthernet, Technology::kWiFi}; |
| EXPECT_TRUE(DefaultSortingOrderIs(ethernet_service, wifi_service)); |
| |
| // Even making the wifi service managed doesn't change the network sorting |
| // order. |
| wifi_service->managed_credentials_ = true; |
| EXPECT_TRUE(DefaultSortingOrderIs(ethernet_service, wifi_service)); |
| } |
| |
| TEST_F(ServiceTest, CompareSources) { |
| // Check that given the exactly same other parameters, |
| // services with different sources are sorted properly. |
| scoped_refptr<MockService> service1(new NiceMock<MockService>(manager())); |
| scoped_refptr<MockService> service2(new NiceMock<MockService>(manager())); |
| service1->source_ = Service::ONCSource::kONCSourceUnknown; |
| service2->source_ = Service::ONCSource::kONCSourceNone; |
| EXPECT_TRUE(DefaultSortingOrderIs(service2, service1)); |
| |
| service1->source_ = Service::ONCSource::kONCSourceUserImport; |
| EXPECT_TRUE(DefaultSortingOrderIs(service1, service2)); |
| |
| service2->source_ = Service::ONCSource::kONCSourceDevicePolicy; |
| EXPECT_TRUE(DefaultSortingOrderIs(service2, service1)); |
| |
| service1->source_ = Service::ONCSource::kONCSourceUserPolicy; |
| EXPECT_TRUE(DefaultSortingOrderIs(service1, service2)); |
| } |
| |
| TEST_F(ServiceTest, SanitizeStorageIdentifier) { |
| EXPECT_EQ("", Service::SanitizeStorageIdentifier("")); |
| |
| for (int c = 0; c < 256; ++c) { |
| std::string identifier(1, c); |
| std::string sanitized_identifier = std::isalnum(c) ? identifier : "_"; |
| EXPECT_EQ(sanitized_identifier, |
| Service::SanitizeStorageIdentifier(identifier)); |
| } |
| |
| EXPECT_EQ("service_1_2_3_2_Fake_Net_", |
| Service::SanitizeStorageIdentifier("service_1-2:3.2_Fake^Net!")); |
| } |
| |
| TEST_F(ServiceTest, DisconnectSetsDisconnectState) { |
| EXPECT_EQ(service_->state(), Service::kStateIdle); |
| |
| // Inactive Service will immediately fail a Disconnect call. |
| Error error; |
| service_->Disconnect(&error, __func__); |
| EXPECT_EQ(error.type(), Error::kNotConnected); |
| EXPECT_EQ(service_->state(), Service::kStateIdle); |
| |
| // Non-disconnectable Service will also immediately fail a Disconnect call. |
| service_->SetDisconnectable(false); |
| error.Reset(); |
| service_->Disconnect(&error, __func__); |
| EXPECT_EQ(error.type(), Error::kNotConnected); |
| EXPECT_EQ(service_->state(), Service::kStateIdle); |
| |
| service_->SetDisconnectable(true); |
| |
| // Otherwise the state will be driven to kStateDisconnecting. |
| service_->SetState(Service::kStateAssociating); |
| error.Reset(); |
| service_->Disconnect(&error, __func__); |
| EXPECT_TRUE(error.IsSuccess()); |
| EXPECT_EQ(service_->state(), Service::kStateDisconnecting); |
| } |
| |
| TEST_F(ServiceTest, DelayedDisconnect) { |
| // Any state that causes IsActive to return true will do. |
| service_->SetState(Service::kStateAssociating); |
| |
| // Begin disconnect but do not finish. |
| Error error; |
| service_->Disconnect(&error, __func__); |
| EXPECT_TRUE(error.IsSuccess()); |
| EXPECT_EQ(service_->state(), Service::kStateDisconnecting); |
| |
| // Trigger connection. |
| ASSERT_TRUE(service_->connectable()); |
| service_->Connect(&error, __func__); |
| EXPECT_TRUE(error.IsSuccess()); |
| EXPECT_EQ(service_->state(), Service::kStateDisconnecting); |
| EXPECT_TRUE(HasPendingConnect()); |
| |
| // Finish the disconnection by driving state to kStateIdle. |
| service_->SetState(Service::kStateIdle); |
| EXPECT_TRUE(HasPendingConnect()); |
| |
| // Invoke the pending disconnect task. |
| GetPendingConnectTask().callback().Run(); |
| EXPECT_FALSE(HasPendingConnect()); |
| } |
| |
| TEST_F(ServiceTest, DelayedDisconnectWithAdditionalConnect) { |
| // Any state that causes IsActive to return true will do. |
| service_->SetState(Service::kStateAssociating); |
| |
| // Begin disconnect but do not finish. |
| Error error; |
| service_->Disconnect(&error, __func__); |
| EXPECT_TRUE(error.IsSuccess()); |
| EXPECT_EQ(service_->state(), Service::kStateDisconnecting); |
| |
| // Trigger connection. |
| ASSERT_TRUE(service_->connectable()); |
| service_->Connect(&error, __func__); |
| EXPECT_TRUE(error.IsSuccess()); |
| EXPECT_EQ(service_->state(), Service::kStateDisconnecting); |
| EXPECT_TRUE(HasPendingConnect()); |
| |
| // Finish the disconnection by driving state to kStateIdle. |
| service_->SetState(Service::kStateIdle); |
| EXPECT_TRUE(HasPendingConnect()); |
| |
| // Trigger connection prior to the pending connect task being run; |
| // ensure that the pending connect task will be cancelled. |
| ASSERT_TRUE(service_->connectable()); |
| service_->Connect(&error, __func__); |
| EXPECT_TRUE(error.IsSuccess()); |
| EXPECT_FALSE(HasPendingConnect()); |
| } |
| |
| TEST_F(ServiceTest, RequestPortalDetection) { |
| SetStateField(Service::kStateConnected); |
| auto network = |
| std::make_unique<MockNetwork>(1, kIfName, Technology::kEthernet); |
| service_->AttachNetwork(network->AsWeakPtr()); |
| EXPECT_CALL(mock_manager_, IsPortalDetectionEnabled).Times(0); |
| EXPECT_CALL(*network, RequestNetworkValidation( |
| NetworkMonitor::ValidationReason::kDBusRequest)); |
| |
| Error error; |
| service_->RequestPortalDetection(&error); |
| EXPECT_TRUE(error.IsSuccess()); |
| } |
| |
| TEST_F(ServiceTest, TrafficCountersRefreshWithMultipleSources) { |
| patchpanel::Client::TrafficCounter counter0; |
| counter0.source = patchpanel::Client::TrafficSource::kChrome; |
| counter0.traffic.rx_bytes = 12; |
| counter0.traffic.tx_bytes = 34; |
| counter0.traffic.rx_packets = 56; |
| counter0.traffic.tx_packets = 78; |
| |
| patchpanel::Client::TrafficCounter counter1; |
| counter1.source = patchpanel::Client::TrafficSource::kUser; |
| counter1.traffic.rx_bytes = 90; |
| counter1.traffic.tx_bytes = 87; |
| counter1.traffic.rx_packets = 65; |
| counter1.traffic.tx_packets = 43; |
| |
| service_->InitializeTrafficCounterSnapshot({counter0, counter1}); |
| EXPECT_EQ(service_->traffic_counter_snapshot().size(), 2); |
| patchpanel::Client::TrafficVector chrome_counters = { |
| .rx_bytes = 12, .tx_bytes = 34, .rx_packets = 56, .tx_packets = 78}; |
| patchpanel::Client::TrafficVector user_counters = { |
| .rx_bytes = 90, .tx_bytes = 87, .rx_packets = 65, .tx_packets = 43}; |
| EXPECT_EQ(service_->traffic_counter_snapshot() |
| [patchpanel::Client::TrafficSource::kChrome], |
| chrome_counters); |
| EXPECT_EQ(service_->traffic_counter_snapshot() |
| [patchpanel::Client::TrafficSource::kUser], |
| user_counters); |
| EXPECT_EQ(service_->current_traffic_counters().size(), 0); |
| |
| counter0.traffic.rx_bytes = 20; |
| counter0.traffic.tx_bytes = 40; |
| counter0.traffic.rx_packets = 60; |
| counter0.traffic.tx_packets = 80; |
| counter1.traffic.rx_bytes = 100; |
| counter1.traffic.tx_bytes = 90; |
| counter1.traffic.rx_packets = 80; |
| counter1.traffic.tx_packets = 70; |
| |
| service_->RefreshTrafficCounters({counter0, counter1}); |
| EXPECT_EQ(service_->traffic_counter_snapshot().size(), 2); |
| chrome_counters = {20, 40, 60, 80}; |
| user_counters = {100, 90, 80, 70}; |
| EXPECT_EQ(service_->traffic_counter_snapshot() |
| [patchpanel::Client::TrafficSource::kChrome], |
| chrome_counters); |
| EXPECT_EQ(service_->traffic_counter_snapshot() |
| [patchpanel::Client::TrafficSource::kUser], |
| user_counters); |
| EXPECT_EQ(service_->current_traffic_counters().size(), 2); |
| patchpanel::Client::TrafficVector chrome_counters_diff = { |
| .rx_bytes = 8, .tx_bytes = 6, .rx_packets = 4, .tx_packets = 2}; |
| patchpanel::Client::TrafficVector user_counters_diff = { |
| .rx_bytes = 10, .tx_bytes = 3, .rx_packets = 15, .tx_packets = 27}; |
| EXPECT_EQ(service_->current_traffic_counters() |
| [patchpanel::Client::TrafficSource::kChrome], |
| chrome_counters_diff); |
| EXPECT_EQ(service_->current_traffic_counters() |
| [patchpanel::Client::TrafficSource::kUser], |
| user_counters_diff); |
| } |
| |
| TEST_F(ServiceTest, TrafficCountersRefreshWithSameSource) { |
| patchpanel::Client::TrafficCounter ipv4_counters; |
| ipv4_counters.source = patchpanel::Client::TrafficSource::kChrome; |
| ipv4_counters.traffic.rx_bytes = 2345; |
| ipv4_counters.traffic.tx_bytes = 723; |
| ipv4_counters.traffic.rx_packets = 10; |
| ipv4_counters.traffic.tx_packets = 20; |
| |
| patchpanel::Client::TrafficCounter ipv6_counters; |
| ipv6_counters.source = patchpanel::Client::TrafficSource::kChrome; |
| ipv6_counters.traffic.rx_bytes = 4592; |
| ipv6_counters.traffic.tx_bytes = 489; |
| ipv6_counters.traffic.rx_packets = 73; |
| ipv6_counters.traffic.tx_packets = 34; |
| |
| service_->InitializeTrafficCounterSnapshot({ipv4_counters, ipv6_counters}); |
| EXPECT_EQ(service_->traffic_counter_snapshot().size(), 1); |
| patchpanel::Client::TrafficVector chrome_counters = { |
| .rx_bytes = 6937, .tx_bytes = 1212, .rx_packets = 83, .tx_packets = 54}; |
| EXPECT_EQ(service_->traffic_counter_snapshot() |
| [patchpanel::Client::TrafficSource::kChrome], |
| chrome_counters); |
| EXPECT_EQ(service_->current_traffic_counters().size(), 0); |
| |
| ipv4_counters.traffic.rx_bytes = 3000; |
| ipv4_counters.traffic.tx_bytes = 800; |
| ipv4_counters.traffic.rx_packets = 30; |
| ipv4_counters.traffic.tx_packets = 20; |
| ipv6_counters.traffic.rx_bytes = 5000; |
| ipv6_counters.traffic.tx_bytes = 500; |
| ipv6_counters.traffic.rx_packets = 80; |
| ipv6_counters.traffic.tx_packets = 40; |
| |
| service_->RefreshTrafficCounters({ipv4_counters, ipv6_counters}); |
| EXPECT_EQ(service_->traffic_counter_snapshot().size(), 1); |
| chrome_counters = {8000, 1300, 110, 60}; |
| EXPECT_EQ(service_->traffic_counter_snapshot() |
| [patchpanel::Client::TrafficSource::kChrome], |
| chrome_counters); |
| EXPECT_EQ(service_->current_traffic_counters().size(), 1); |
| patchpanel::Client::TrafficVector chrome_counters_diff = { |
| .rx_bytes = 1063, .tx_bytes = 88, .rx_packets = 27, .tx_packets = 6}; |
| EXPECT_EQ(service_->current_traffic_counters() |
| [patchpanel::Client::TrafficSource::kChrome], |
| chrome_counters_diff); |
| } |
| |
| TEST_F(ServiceTest, RequestTrafficCounters) { |
| auto source0 = patchpanel::Client::TrafficSource::kChrome; |
| auto source1 = patchpanel::Client::TrafficSource::kUser; |
| |
| patchpanel::Client::TrafficVector init_counter_arr0 = {0, 0, 0, 0}; |
| patchpanel::Client::TrafficVector init_counter_arr1 = {0, 0, 0, 0}; |
| patchpanel::Client::TrafficCounter init_counter0 = |
| CreateCounter(init_counter_arr0, source0, kIfName); |
| patchpanel::Client::TrafficCounter init_counter1 = |
| CreateCounter(init_counter_arr1, source1, kIfName); |
| |
| service_->InitializeTrafficCounterSnapshot({init_counter0, init_counter1}); |
| |
| patchpanel::Client::TrafficVector counter_arr0 = { |
| .rx_bytes = 12, .tx_bytes = 34, .rx_packets = 56, .tx_packets = 78}; |
| patchpanel::Client::TrafficVector counter_arr1 = { |
| .rx_bytes = 90, .tx_bytes = 87, .rx_packets = 65, .tx_packets = 43}; |
| patchpanel::Client::TrafficCounter counter0 = |
| CreateCounter(counter_arr0, source0, kIfName); |
| patchpanel::Client::TrafficCounter counter1 = |
| CreateCounter(counter_arr1, source1, kIfName); |
| |
| std::vector<patchpanel::Client::TrafficCounter> counters{counter0, counter1}; |
| |
| auto client = std::make_unique<patchpanel::FakeClient>(); |
| patchpanel::FakeClient* patchpanel_client = client.get(); |
| mock_manager_.set_patchpanel_client_for_testing(std::move(client)); |
| |
| patchpanel_client->set_stored_traffic_counters(counters); |
| |
| scoped_refptr<MockDevice> mock_device = new MockDevice( |
| &mock_manager_, kIfName, |
| net_base::MacAddress(0x00, 0x01, 0x02, 0x03, 0x04, 0x05), 0); |
| mock_device->set_selected_service_for_testing(service_); |
| ON_CALL(mock_manager_, FindDeviceFromService(_)) |
| .WillByDefault(Return(mock_device)); |
| |
| bool successfully_requested_traffic_counters = false; |
| std::vector<brillo::VariantDictionary> actual_traffic_counters; |
| service_->RequestTrafficCounters(base::BindOnce( |
| [](bool* success, std::vector<brillo::VariantDictionary>* output, |
| const Error& error, |
| const std::vector<brillo::VariantDictionary>& input) { |
| *success = error.IsSuccess(); |
| output->assign(input.begin(), input.end()); |
| }, |
| &successfully_requested_traffic_counters, &actual_traffic_counters)); |
| |
| EXPECT_TRUE(successfully_requested_traffic_counters); |
| for (const auto& dict : actual_traffic_counters) { |
| EXPECT_EQ(3, dict.size()); |
| EXPECT_TRUE(base::Contains(dict, "source")); |
| EXPECT_TRUE(base::Contains(dict, "rx_bytes")); |
| EXPECT_TRUE(base::Contains(dict, "tx_bytes")); |
| |
| if (dict.at("source").TryGet<std::string>() == "CHROME") { |
| EXPECT_EQ(12, dict.at("rx_bytes").TryGet<uint64_t>()); |
| EXPECT_EQ(34, dict.at("tx_bytes").TryGet<uint64_t>()); |
| continue; |
| } |
| |
| if (dict.at("source").TryGet<std::string>() == "USER") { |
| EXPECT_EQ(90, dict.at("rx_bytes").TryGet<uint64_t>()); |
| EXPECT_EQ(87, dict.at("tx_bytes").TryGet<uint64_t>()); |
| continue; |
| } |
| |
| FAIL() << "Unxpected source " << dict.at("source").TryGet<std::string>(); |
| } |
| } |
| |
| TEST_F(ServiceTest, ResetTrafficCounters) { |
| auto source0 = patchpanel::Client::TrafficSource::kChrome; |
| auto source1 = patchpanel::Client::TrafficSource::kUser; |
| |
| // Initialize the Service's traffic counter snapshot. |
| patchpanel::Client::TrafficVector init_counter_arr0 = { |
| .rx_bytes = 10, .tx_bytes = 20, .rx_packets = 30, .tx_packets = 40}; |
| patchpanel::Client::TrafficVector init_counter_arr1 = { |
| .rx_bytes = 50, .tx_bytes = 60, .rx_packets = 70, .tx_packets = 80}; |
| patchpanel::Client::TrafficCounter init_counter0 = |
| CreateCounter(init_counter_arr0, source0, kIfName); |
| patchpanel::Client::TrafficCounter init_counter1 = |
| CreateCounter(init_counter_arr1, source1, kIfName); |
| service_->InitializeTrafficCounterSnapshot({init_counter0, init_counter1}); |
| |
| // Refresh traffic counters, updating the traffic counter snapshot and current |
| // traffic counters. |
| patchpanel::Client::TrafficVector counter_arr0 = { |
| .rx_bytes = 100, .tx_bytes = 200, .rx_packets = 300, .tx_packets = 400}; |
| patchpanel::Client::TrafficVector counter_arr1 = { |
| .rx_bytes = 500, .tx_bytes = 600, .rx_packets = 700, .tx_packets = 800}; |
| patchpanel::Client::TrafficCounter counter0 = |
| CreateCounter(counter_arr0, source0, kIfName); |
| patchpanel::Client::TrafficCounter counter1 = |
| CreateCounter(counter_arr1, source1, kIfName); |
| service_->RefreshTrafficCounters({counter0, counter1}); |
| EXPECT_EQ(service_->traffic_counter_snapshot().size(), 2); |
| EXPECT_EQ(service_->traffic_counter_snapshot() |
| [patchpanel::Client::TrafficSource::kChrome], |
| counter_arr0); |
| EXPECT_EQ(service_->traffic_counter_snapshot() |
| [patchpanel::Client::TrafficSource::kUser], |
| counter_arr1); |
| EXPECT_EQ(service_->current_traffic_counters().size(), 2); |
| patchpanel::Client::TrafficVector chrome_counters_diff = { |
| .rx_bytes = 90, .tx_bytes = 180, .rx_packets = 270, .tx_packets = 360}; |
| patchpanel::Client::TrafficVector user_counters_diff = { |
| .rx_bytes = 450, .tx_bytes = 540, .rx_packets = 630, .tx_packets = 720}; |
| EXPECT_EQ(service_->current_traffic_counters() |
| [patchpanel::Client::TrafficSource::kChrome], |
| chrome_counters_diff); |
| EXPECT_EQ(service_->current_traffic_counters() |
| [patchpanel::Client::TrafficSource::kUser], |
| user_counters_diff); |
| |
| // Reset the traffic counters. |
| service_->ResetTrafficCounters(/*error=*/nullptr); |
| EXPECT_EQ(service_->current_traffic_counters().size(), 0); |
| EXPECT_EQ(service_->traffic_counter_snapshot().size(), 2); |
| EXPECT_EQ(service_->traffic_counter_snapshot() |
| [patchpanel::Client::TrafficSource::kChrome], |
| counter_arr0); |
| EXPECT_EQ(service_->traffic_counter_snapshot() |
| [patchpanel::Client::TrafficSource::kUser], |
| counter_arr1); |
| |
| // Refresh traffic counters, updating the traffic counter snapshot and current |
| // traffic counters. |
| counter_arr0 = {.rx_bytes = 1000, |
| .tx_bytes = 2000, |
| .rx_packets = 3000, |
| .tx_packets = 4000}; |
| counter_arr1 = {.rx_bytes = 5000, |
| .tx_bytes = 6000, |
| .rx_packets = 7000, |
| .tx_packets = 8000}; |
| counter0 = CreateCounter(counter_arr0, source0, kIfName); |
| counter1 = CreateCounter(counter_arr1, source1, kIfName); |
| service_->RefreshTrafficCounters({counter0, counter1}); |
| EXPECT_EQ(service_->traffic_counter_snapshot().size(), 2); |
| EXPECT_EQ(service_->traffic_counter_snapshot() |
| [patchpanel::Client::TrafficSource::kChrome], |
| counter_arr0); |
| EXPECT_EQ(service_->traffic_counter_snapshot() |
| [patchpanel::Client::TrafficSource::kUser], |
| counter_arr1); |
| EXPECT_EQ(service_->current_traffic_counters().size(), 2); |
| chrome_counters_diff = {.rx_bytes = 900, |
| .tx_bytes = 1800, |
| .rx_packets = 2700, |
| .tx_packets = 3600}; |
| user_counters_diff = {.rx_bytes = 4500, |
| .tx_bytes = 5400, |
| .rx_packets = 6300, |
| .tx_packets = 7200}; |
| EXPECT_EQ(service_->current_traffic_counters() |
| [patchpanel::Client::TrafficSource::kChrome], |
| chrome_counters_diff); |
| EXPECT_EQ(service_->current_traffic_counters() |
| [patchpanel::Client::TrafficSource::kUser], |
| user_counters_diff); |
| } |
| |
| TEST_F(ServiceTest, TrafficCountersRefreshCounterDecreasing) { |
| patchpanel::Client::TrafficCounter ipv4_counters; |
| ipv4_counters.source = patchpanel::Client::TrafficSource::kChrome; |
| ipv4_counters.traffic.rx_bytes = 2345; |
| ipv4_counters.traffic.tx_bytes = 723; |
| ipv4_counters.traffic.rx_packets = 10; |
| ipv4_counters.traffic.tx_packets = 20; |
| |
| patchpanel::Client::TrafficCounter ipv6_counters; |
| ipv6_counters.source = patchpanel::Client::TrafficSource::kChrome; |
| ipv6_counters.traffic.rx_bytes = 4592; |
| ipv6_counters.traffic.tx_bytes = 489; |
| ipv6_counters.traffic.rx_packets = 73; |
| ipv6_counters.traffic.tx_packets = 34; |
| |
| service_->InitializeTrafficCounterSnapshot({ipv4_counters, ipv6_counters}); |
| EXPECT_EQ(service_->traffic_counter_snapshot().size(), 1); |
| patchpanel::Client::TrafficVector chrome_counters = { |
| .rx_bytes = 6937, .tx_bytes = 1212, .rx_packets = 83, .tx_packets = 54}; |
| EXPECT_EQ(service_->traffic_counter_snapshot() |
| [patchpanel::Client::TrafficSource::kChrome], |
| chrome_counters); |
| EXPECT_EQ(service_->current_traffic_counters().size(), 0); |
| |
| ipv4_counters.traffic.rx_bytes = 3000; |
| ipv4_counters.traffic.tx_bytes = 800; |
| ipv4_counters.traffic.rx_packets = 30; |
| ipv4_counters.traffic.tx_packets = 20; |
| ipv6_counters.traffic.rx_bytes = 1000; |
| ipv6_counters.traffic.tx_bytes = 500; |
| ipv6_counters.traffic.rx_packets = 60; |
| ipv6_counters.traffic.tx_packets = 40; |
| |
| service_->RefreshTrafficCounters({ipv4_counters, ipv6_counters}); |
| EXPECT_EQ(service_->traffic_counter_snapshot().size(), 1); |
| chrome_counters = {4000, 1300, 90, 60}; |
| EXPECT_EQ(service_->traffic_counter_snapshot() |
| [patchpanel::Client::TrafficSource::kChrome], |
| chrome_counters); |
| EXPECT_EQ(service_->current_traffic_counters().size(), 1); |
| // If any of the packet counters decreased from last snapshot, assume a |
| // counter reset happened, and treat all new counters as diff from 0. |
| patchpanel::Client::TrafficVector chrome_counters_diff = { |
| .rx_bytes = 4000, .tx_bytes = 1300, .rx_packets = 90, .tx_packets = 60}; |
| EXPECT_EQ(service_->current_traffic_counters() |
| [patchpanel::Client::TrafficSource::kChrome], |
| chrome_counters_diff); |
| } |
| |
| TEST_F(ServiceTest, UpdateLinkSpeed) { |
| EXPECT_CALL(*GetAdaptor(), EmitIntChanged(kUplinkSpeedPropertyKbps, 10)) |
| .Times(1); |
| EXPECT_CALL(*GetAdaptor(), EmitIntChanged(kDownlinkSpeedPropertyKbps, 20)) |
| .Times(1); |
| |
| SetUplinkSpeedKbps(10); |
| SetDownlinkSpeedKbps(20); |
| |
| EXPECT_EQ(uplink_speed_kbps(), 10); |
| EXPECT_EQ(downlink_speed_kbps(), 20); |
| } |
| |
| TEST_F(ServiceTest, UpdateLinkSpeedTwice) { |
| // Check link speed is only set once when the setter is called twice on |
| // identical value. |
| EXPECT_CALL(*GetAdaptor(), EmitIntChanged(kUplinkSpeedPropertyKbps, _)) |
| .Times(1); |
| |
| SetUplinkSpeedKbps(30); |
| SetUplinkSpeedKbps(30); |
| |
| EXPECT_EQ(uplink_speed_kbps(), 30); |
| } |
| |
| TEST_F(ServiceTest, ServiceMetricsTimeToConfig) { |
| EXPECT_CALL(*metrics(), SendToUMA("Network.Shill.Wifi.TimeToConfig", Ge(0), |
| Metrics::kTimerHistogramMillisecondsMin, |
| Metrics::kTimerHistogramMillisecondsMax, |
| Metrics::kTimerHistogramNumBuckets)); |
| service_->UpdateStateTransitionMetrics(Service::kStateConfiguring); |
| service_->UpdateStateTransitionMetrics(Service::kStateConnected); |
| } |
| |
| TEST_F(ServiceTest, ServiceMetricsTimeToPortal) { |
| EXPECT_CALL(*metrics(), SendToUMA("Network.Shill.Wifi.TimeToPortal", Ge(0), |
| Metrics::kTimerHistogramMillisecondsMin, |
| Metrics::kTimerHistogramMillisecondsMax, |
| Metrics::kTimerHistogramNumBuckets)); |
| service_->UpdateStateTransitionMetrics(Service::kStateConnected); |
| service_->UpdateStateTransitionMetrics(Service::kStateNoConnectivity); |
| } |
| |
| TEST_F(ServiceTest, ServiceMetricsTimeToOnline) { |
| EXPECT_CALL(*metrics(), SendToUMA("Network.Shill.Wifi.TimeToOnline", Ge(0), |
| Metrics::kTimerHistogramMillisecondsMin, |
| Metrics::kTimerHistogramMillisecondsMax, |
| Metrics::kTimerHistogramNumBuckets)); |
| service_->UpdateStateTransitionMetrics(Service::kStateConnected); |
| service_->UpdateStateTransitionMetrics(Service::kStateOnline); |
| } |
| |
| TEST_F(ServiceTest, ServiceMetricsServiceFailure) { |
| service_->SetFailure(Service::kFailureBadPassphrase); |
| EXPECT_CALL( |
| *metrics(), |
| SendEnumToUMA(Metrics::kMetricNetworkServiceError, Technology::kWiFi, |
| Metrics::kNetworkServiceErrorBadPassphrase)); |
| service_->UpdateStateTransitionMetrics(Service::kStateFailure); |
| } |
| |
| TEST_F(ServiceTest, PortalDetectionStops) { |
| SetStateField(Service::kStateConnected); |
| auto network = std::make_unique<MockNetwork>(1, "wlan0", Technology::kWiFi); |
| service_->AttachNetwork(network->AsWeakPtr()); |
| |
| service_->network_event_handler()->OnNetworkValidationStop( |
| 1, /*is_failure=*/false); |
| EXPECT_EQ(Service::kStateOnline, service_->state()); |
| } |
| |
| TEST_F(ServiceTest, PortalDetectionFailsAndStops) { |
| SetStateField(Service::kStateConnected); |
| auto network = std::make_unique<MockNetwork>(1, "wlan0", Technology::kWiFi); |
| service_->AttachNetwork(network->AsWeakPtr()); |
| |
| service_->network_event_handler()->OnNetworkValidationStop( |
| 1, /*is_failure=*/true); |
| EXPECT_EQ(Service::kStateNoConnectivity, service_->state()); |
| } |
| |
| TEST_F(ServiceTest, PortalDetectionStopsAfterDisconnection) { |
| SetStateField(Service::kStateIdle); |
| auto network = std::make_unique<MockNetwork>(1, "wlan0", Technology::kWiFi); |
| service_->AttachNetwork(network->AsWeakPtr()); |
| |
| service_->network_event_handler()->OnNetworkValidationStop( |
| 1, /*is_failure=*/false); |
| EXPECT_EQ(Service::kStateIdle, service_->state()); |
| } |
| |
| TEST_F(ServiceTest, PortalDetectionResult_AfterDisconnection) { |
| SetStateField(Service::kStateIdle); |
| auto network = std::make_unique<MockNetwork>(1, "wlan0", Technology::kWiFi); |
| service_->AttachNetwork(network->AsWeakPtr()); |
| |
| const NetworkMonitor::Result result{ |
| .num_attempts = 1, |
| .validation_state = |
| PortalDetector::ValidationState::kInternetConnectivity, |
| .probe_result_metric = Metrics::kPortalDetectorResultOnline, |
| }; |
| |
| ASSERT_NE(service_->network_event_handler(), nullptr); |
| service_->network_event_handler()->OnNetworkValidationResult(1, result); |
| |
| EXPECT_EQ(Service::kStateIdle, service_->state()); |
| EXPECT_EQ("", service_->probe_url_string()); |
| } |
| |
| TEST_F(ServiceTest, PortalDetectionResult_Online) { |
| SetStateField(Service::kStateOnline); |
| auto network = std::make_unique<MockNetwork>(1, "wlan0", Technology::kWiFi); |
| service_->AttachNetwork(network->AsWeakPtr()); |
| |
| const NetworkMonitor::Result result{ |
| .num_attempts = 1, |
| .validation_state = |
| PortalDetector::ValidationState::kInternetConnectivity, |
| .probe_result_metric = Metrics::kPortalDetectorResultOnline, |
| }; |
| service_->network_event_handler()->OnNetworkValidationResult(1, result); |
| |
| EXPECT_EQ(Service::kStateOnline, service_->state()); |
| EXPECT_EQ("", service_->probe_url_string()); |
| } |
| |
| TEST_F(ServiceTest, PortalDetectionResult_OnlineSecondTry) { |
| SetStateField(Service::kStateOnline); |
| auto network = std::make_unique<MockNetwork>(1, "wlan0", Technology::kWiFi); |
| service_->AttachNetwork(network->AsWeakPtr()); |
| |
| const NetworkMonitor::Result result{ |
| .num_attempts = 1, |
| .validation_state = |
| PortalDetector::ValidationState::kInternetConnectivity, |
| .probe_result_metric = Metrics::kPortalDetectorResultOnline, |
| }; |
| service_->network_event_handler()->OnNetworkValidationResult(1, result); |
| |
| EXPECT_EQ(Service::kStateOnline, service_->state()); |
| EXPECT_EQ("", service_->probe_url_string()); |
| } |
| |
| TEST_F(ServiceTest, PortalDetectionResult_ProbeConnectionFailure) { |
| SetStateField(Service::kStateOnline); |
| auto network = std::make_unique<MockNetwork>(1, "wlan0", Technology::kWiFi); |
| service_->AttachNetwork(network->AsWeakPtr()); |
| |
| const NetworkMonitor::Result result{ |
| .num_attempts = 1, |
| .validation_state = PortalDetector::ValidationState::kNoConnectivity, |
| .probe_result_metric = Metrics::kPortalDetectorResultConnectionFailure, |
| }; |
| service_->network_event_handler()->OnNetworkValidationResult(1, result); |
| |
| EXPECT_EQ(Service::kStateNoConnectivity, service_->state()); |
| EXPECT_EQ("", service_->probe_url_string()); |
| } |
| |
| TEST_F(ServiceTest, PortalDetectionResult_DNSFailure) { |
| SetStateField(Service::kStateOnline); |
| auto network = std::make_unique<MockNetwork>(1, "wlan0", Technology::kWiFi); |
| service_->AttachNetwork(network->AsWeakPtr()); |
| |
| const NetworkMonitor::Result result{ |
| .num_attempts = 1, |
| .validation_state = PortalDetector::ValidationState::kNoConnectivity, |
| .probe_result_metric = Metrics::kPortalDetectorResultDNSFailure, |
| }; |
| service_->network_event_handler()->OnNetworkValidationResult(1, result); |
| |
| EXPECT_EQ(Service::kStateNoConnectivity, service_->state()); |
| EXPECT_EQ("", service_->probe_url_string()); |
| } |
| |
| TEST_F(ServiceTest, PortalDetectionResult_DNSTimeout) { |
| SetStateField(Service::kStateOnline); |
| auto network = std::make_unique<MockNetwork>(1, "wlan0", Technology::kWiFi); |
| service_->AttachNetwork(network->AsWeakPtr()); |
| |
| const NetworkMonitor::Result result{ |
| .num_attempts = 1, |
| .validation_state = PortalDetector::ValidationState::kNoConnectivity, |
| .probe_result_metric = Metrics::kPortalDetectorResultDNSTimeout, |
| }; |
| service_->network_event_handler()->OnNetworkValidationResult(1, result); |
| |
| EXPECT_EQ(Service::kStateNoConnectivity, service_->state()); |
| EXPECT_EQ("", service_->probe_url_string()); |
| } |
| |
| TEST_F(ServiceTest, PortalDetectionResult_Redirect) { |
| SetStateField(Service::kStateOnline); |
| auto network = std::make_unique<MockNetwork>(1, "wlan0", Technology::kWiFi); |
| service_->AttachNetwork(network->AsWeakPtr()); |
| |
| const NetworkMonitor::Result result{ |
| .num_attempts = 1, |
| .validation_state = PortalDetector::ValidationState::kPortalRedirect, |
| .probe_result_metric = Metrics::kPortalDetectorResultRedirectFound, |
| .target_url = |
| net_base::HttpUrl::CreateFromString(PortalDetector::kDefaultHttpUrl), |
| }; |
| service_->network_event_handler()->OnNetworkValidationResult(1, result); |
| |
| EXPECT_EQ(Service::kStateRedirectFound, service_->state()); |
| EXPECT_EQ(PortalDetector::kDefaultHttpUrl, service_->probe_url_string()); |
| } |
| |
| TEST_F(ServiceTest, PortalDetectionResult_RedirectNoUrl) { |
| SetStateField(Service::kStateOnline); |
| auto network = std::make_unique<MockNetwork>(1, "wlan0", Technology::kWiFi); |
| service_->AttachNetwork(network->AsWeakPtr()); |
| |
| const NetworkMonitor::Result result{ |
| .num_attempts = 1, |
| .validation_state = PortalDetector::ValidationState::kPortalSuspected, |
| .probe_result_metric = Metrics::kPortalDetectorResultRedirectNoUrl, |
| }; |
| service_->network_event_handler()->OnNetworkValidationResult(1, result); |
| |
| EXPECT_EQ(Service::kStateRedirectFound, service_->state()); |
| EXPECT_EQ("", service_->probe_url_string()); |
| } |
| |
| TEST_F(ServiceTest, PortalDetectionResult_PortalSuspected) { |
| SetStateField(Service::kStateOnline); |
| auto network = std::make_unique<MockNetwork>(1, "wlan0", Technology::kWiFi); |
| service_->AttachNetwork(network->AsWeakPtr()); |
| |
| const NetworkMonitor::Result result{ |
| .num_attempts = 1, |
| .validation_state = PortalDetector::ValidationState::kPortalSuspected, |
| .probe_result_metric = Metrics::kPortalDetectorResultHTTPSFailure, |
| }; |
| service_->network_event_handler()->OnNetworkValidationResult(1, result); |
| |
| EXPECT_EQ(Service::kStateRedirectFound, service_->state()); |
| EXPECT_EQ("", service_->probe_url_string()); |
| } |
| |
| TEST_F(ServiceTest, PortalDetectionResult_NoConnectivity) { |
| SetStateField(Service::kStateOnline); |
| auto network = std::make_unique<MockNetwork>(1, "wlan0", Technology::kWiFi); |
| service_->AttachNetwork(network->AsWeakPtr()); |
| |
| const NetworkMonitor::Result result{ |
| .num_attempts = 1, |
| .validation_state = PortalDetector::ValidationState::kNoConnectivity, |
| .probe_result_metric = Metrics::kPortalDetectorResultUnknown, |
| }; |
| service_->network_event_handler()->OnNetworkValidationResult(1, result); |
| |
| EXPECT_EQ(Service::kStateNoConnectivity, service_->state()); |
| EXPECT_EQ("", service_->probe_url_string()); |
| } |
| |
| TEST_F(ServiceTest, UpdateNetworkValidationMode) { |
| auto network = |
| std::make_unique<MockNetwork>(1, "test_ifname", Technology::kEthernet); |
| service_->AttachNetwork(network->AsWeakPtr()); |
| ON_CALL(mock_manager_, IsPortalDetectionEnabled).WillByDefault(Return(true)); |
| |
| EXPECT_EQ(NetworkMonitor::ValidationMode::kFullValidation, |
| service_->GetNetworkValidationMode()); |
| EXPECT_CALL(*network, UpdateNetworkValidationMode( |
| NetworkMonitor::ValidationMode::kFullValidation)); |
| service_->UpdateNetworkValidationMode(); |
| } |
| |
| TEST_F(ServiceTest, UpdateNetworkValidationModeWhenDisabledByTechnology) { |
| auto network = |
| std::make_unique<MockNetwork>(1, "test_ifname", Technology::kEthernet); |
| service_->AttachNetwork(network->AsWeakPtr()); |
| ON_CALL(mock_manager_, IsPortalDetectionEnabled).WillByDefault(Return(false)); |
| |
| EXPECT_CALL(*network, UpdateNetworkValidationMode( |
| NetworkMonitor::ValidationMode::kDisabled)); |
| EXPECT_EQ(NetworkMonitor::ValidationMode::kDisabled, |
| service_->GetNetworkValidationMode()); |
| |
| service_->UpdateNetworkValidationMode(); |
| EXPECT_EQ(Service::kStateOnline, service_->state()); |
| } |
| |
| TEST_F(ServiceTest, UpdateNetworkValidationModeWhenDisabledByProxy) { |
| Error error; |
| service_->SetProxyConfig("proxyconfiguration", &error); |
| auto network = |
| std::make_unique<MockNetwork>(1, "test_ifname", Technology::kEthernet); |
| service_->AttachNetwork(network->AsWeakPtr()); |
| ON_CALL(mock_manager_, IsPortalDetectionEnabled).WillByDefault(Return(true)); |
| |
| EXPECT_CALL(*network, UpdateNetworkValidationMode( |
| NetworkMonitor::ValidationMode::kHTTPOnly)); |
| EXPECT_EQ(NetworkMonitor::ValidationMode::kHTTPOnly, |
| service_->GetNetworkValidationMode()); |
| |
| service_->UpdateNetworkValidationMode(); |
| EXPECT_NE(Service::kStateOnline, service_->state()); |
| } |
| |
| TEST_F(ServiceTest, UpdateNetworkValidationModeWhenDisabledByCheckPortal) { |
| Error error; |
| service_->SetCheckPortal("false", &error); |
| auto network = |
| std::make_unique<MockNetwork>(1, "test_ifname", Technology::kEthernet); |
| service_->AttachNetwork(network->AsWeakPtr()); |
| ON_CALL(mock_manager_, IsPortalDetectionEnabled).WillByDefault(Return(true)); |
| |
| EXPECT_CALL(*network, UpdateNetworkValidationMode( |
| NetworkMonitor::ValidationMode::kDisabled)); |
| EXPECT_EQ(NetworkMonitor::ValidationMode::kDisabled, |
| service_->GetNetworkValidationMode()); |
| |
| service_->UpdateNetworkValidationMode(); |
| EXPECT_EQ(Service::kStateOnline, service_->state()); |
| } |
| |
| TEST_F(ServiceTest, UpdateNetworkValidationModeWhenSetToHTTPOnly) { |
| Error error; |
| service_->SetCheckPortal("http-only", &error); |
| auto network = |
| std::make_unique<MockNetwork>(1, "test_ifname", Technology::kEthernet); |
| service_->AttachNetwork(network->AsWeakPtr()); |
| EXPECT_CALL(mock_manager_, IsPortalDetectionEnabled).Times(0); |
| |
| EXPECT_CALL(*network, UpdateNetworkValidationMode( |
| NetworkMonitor::ValidationMode::kHTTPOnly)); |
| EXPECT_EQ(NetworkMonitor::ValidationMode::kHTTPOnly, |
| service_->GetNetworkValidationMode()); |
| |
| service_->UpdateNetworkValidationMode(); |
| } |
| |
| TEST_F(ServiceTest, UpdateNetworkValidationSucceeds) { |
| SetStateField(Service::kStateConnected); |
| ON_CALL(mock_manager_, IsPortalDetectionEnabled(_)) |
| .WillByDefault(Return(true)); |
| auto network = |
| std::make_unique<MockNetwork>(1, kIfName, Technology::kEthernet); |
| service_->AttachNetwork(network->AsWeakPtr()); |
| |
| EXPECT_CALL(*network, UpdateNetworkValidationMode( |
| NetworkMonitor::ValidationMode::kFullValidation)); |
| service_->UpdateNetworkValidationMode(); |
| EXPECT_EQ(Service::kStateConnected, service_->state()); |
| } |
| |
| TEST_F(ServiceTest, UpdateNetworkValidationFails) { |
| SetStateField(Service::kStateConnected); |
| ON_CALL(mock_manager_, IsPortalDetectionEnabled(_)) |
| .WillByDefault(Return(true)); |
| auto network = |
| std::make_unique<MockNetwork>(1, kIfName, Technology::kEthernet); |
| service_->AttachNetwork(network->AsWeakPtr()); |
| |
| EXPECT_CALL(*network, UpdateNetworkValidationMode( |
| NetworkMonitor::ValidationMode::kFullValidation)); |
| service_->UpdateNetworkValidationMode(); |
| EXPECT_EQ(Service::kStateConnected, service_->state()); |
| |
| service_->network_event_handler()->OnNetworkValidationStart( |
| 1, /*is_failure=*/true); |
| EXPECT_EQ(Service::kStateNoConnectivity, service_->state()); |
| } |
| |
| TEST_F(ServiceTest, CheckPortalStateNames) { |
| EXPECT_EQ("true", Service::CheckPortalStateToString( |
| Service::CheckPortalState::kTrue)); |
| EXPECT_EQ("false", Service::CheckPortalStateToString( |
| Service::CheckPortalState::kFalse)); |
| EXPECT_EQ("auto", Service::CheckPortalStateToString( |
| Service::CheckPortalState::kAutomatic)); |
| } |
| |
| class ServiceNetworkConfigTest : public ServiceTest { |
| protected: |
| static constexpr char kIPv4Addr[] = "1.2.3.4/24"; |
| static constexpr char kIPv4Gateway[] = "1.2.3.5"; |
| static constexpr char kIPv6Addr1[] = "fd00::1/64"; |
| static constexpr char kIPv6Addr2[] = "fd00::2/64"; |
| static constexpr char kIPv6Gateway[] = "fd00::3"; |
| static constexpr char kDNS1[] = "4.3.2.1"; |
| static constexpr char kDNS2[] = "4.3.2.2"; |
| static constexpr char kDNS3[] = "fdfd::1"; |
| static constexpr char kSearchDomain1[] = "domain1"; |
| static constexpr char kSearchDomain2[] = "domain2"; |
| static constexpr char kIncludedRoute1[] = "10.10.10.0/24"; |
| static constexpr char kIncludedRoute2[] = "fd01::/64"; |
| static constexpr char kExcludedRoute1[] = "fd02::/128"; |
| static constexpr char kExcludedRoute2[] = "10.20.30.0/24"; |
| static constexpr int kMTU = 1400; |
| |
| ServiceNetworkConfigTest() |
| : network_(Network::CreateForTesting(1, |
| "test_ifname", |
| Technology::kEthernet, |
| false, |
| nullptr, |
| nullptr, |
| nullptr, |
| nullptr)) {} |
| |
| void SetNetworkConfigOnNetwork(const NetworkConfig& config) { |
| // In this test we only need to update the merged NetworkConfig on the |
| // network object. It doesn't matter which NetworkConfig we're setting here. |
| network_->set_link_protocol_network_config( |
| std::make_unique<NetworkConfig>(config)); |
| } |
| |
| brillo::VariantDictionary GetNetworkConfigOnService() { |
| brillo::VariantDictionary props; |
| Error error; |
| service_->store().GetProperties(&props, &error); |
| return props[kNetworkConfigProperty].Get<brillo::VariantDictionary>(); |
| } |
| |
| std::unique_ptr<Network> network_; |
| }; |
| |
| TEST_F(ServiceNetworkConfigTest, NonEmptyValues) { |
| service_->AttachNetwork(network_->AsWeakPtr()); |
| |
| NetworkConfig config; |
| config.ipv4_address = *IPv4CIDR::CreateFromCIDRString(kIPv4Addr); |
| config.ipv4_gateway = *IPv4Address::CreateFromString(kIPv4Gateway); |
| config.ipv6_addresses = { |
| *IPv6CIDR::CreateFromCIDRString(kIPv6Addr1), |
| *IPv6CIDR::CreateFromCIDRString(kIPv6Addr2), |
| }; |
| config.ipv6_gateway = *IPv6Address::CreateFromString(kIPv6Gateway); |
| config.dns_servers = { |
| *IPAddress::CreateFromString(kDNS1), |
| *IPAddress::CreateFromString(kDNS2), |
| *IPAddress::CreateFromString(kDNS3), |
| }; |
| config.dns_search_domains = {kSearchDomain1, kSearchDomain2}; |
| config.included_route_prefixes = { |
| *IPCIDR::CreateFromCIDRString(kIncludedRoute1), |
| *IPCIDR::CreateFromCIDRString(kIncludedRoute2), |
| }; |
| config.excluded_route_prefixes = { |
| *IPCIDR::CreateFromCIDRString(kExcludedRoute1), |
| *IPCIDR::CreateFromCIDRString(kExcludedRoute2), |
| }; |
| config.mtu = kMTU; |
| |
| SetNetworkConfigOnNetwork(config); |
| |
| brillo::VariantDictionary actual = GetNetworkConfigOnService(); |
| |
| EXPECT_EQ(actual[kNetworkConfigIPv4AddressProperty].Get<std::string>(), |
| kIPv4Addr); |
| EXPECT_EQ(actual[kNetworkConfigIPv4GatewayProperty].Get<std::string>(), |
| kIPv4Gateway); |
| EXPECT_EQ(actual[kNetworkConfigIPv6AddressesProperty] |
| .Get<std::vector<std::string>>(), |
| std::vector<std::string>({kIPv6Addr1, kIPv6Addr2})); |
| EXPECT_EQ(actual[kNetworkConfigIPv6GatewayProperty].Get<std::string>(), |
| kIPv6Gateway); |
| EXPECT_EQ( |
| actual[kNetworkConfigNameServersProperty].Get<std::vector<std::string>>(), |
| std::vector<std::string>({kDNS1, kDNS2, kDNS3})); |
| EXPECT_EQ(actual[kNetworkConfigSearchDomainsProperty] |
| .Get<std::vector<std::string>>(), |
| std::vector<std::string>({kSearchDomain1, kSearchDomain2})); |
| EXPECT_EQ(actual[kNetworkConfigIncludedRoutesProperty], |
| std::vector<std::string>({kIncludedRoute1, kIncludedRoute2})); |
| EXPECT_EQ(actual[kNetworkConfigExcludedRoutesProperty], |
| std::vector<std::string>({kExcludedRoute1, kExcludedRoute2})); |
| EXPECT_EQ(actual[kNetworkConfigMTUProperty], kMTU); |
| } |
| |
| TEST_F(ServiceNetworkConfigTest, DefaultValues) { |
| service_->AttachNetwork(network_->AsWeakPtr()); |
| |
| brillo::VariantDictionary actual = GetNetworkConfigOnService(); |
| |
| EXPECT_EQ(actual[kNetworkConfigIPv4AddressProperty].Get<std::string>(), ""); |
| EXPECT_EQ(actual[kNetworkConfigIPv4GatewayProperty].Get<std::string>(), ""); |
| EXPECT_EQ(actual[kNetworkConfigIPv6AddressesProperty] |
| .Get<std::vector<std::string>>(), |
| std::vector<std::string>()); |
| EXPECT_EQ(actual[kNetworkConfigIPv6GatewayProperty].Get<std::string>(), ""); |
| EXPECT_EQ( |
| actual[kNetworkConfigNameServersProperty].Get<std::vector<std::string>>(), |
| std::vector<std::string>()); |
| EXPECT_EQ(actual[kNetworkConfigSearchDomainsProperty] |
| .Get<std::vector<std::string>>(), |
| std::vector<std::string>()); |
| EXPECT_EQ(actual[kNetworkConfigIncludedRoutesProperty], |
| std::vector<std::string>()); |
| EXPECT_EQ(actual[kNetworkConfigExcludedRoutesProperty], |
| std::vector<std::string>()); |
| EXPECT_EQ(actual[kNetworkConfigMTUProperty], 0); |
| } |
| |
| TEST_F(ServiceNetworkConfigTest, EmptyNetworkConfig) { |
| brillo::VariantDictionary actual = GetNetworkConfigOnService(); |
| EXPECT_EQ(0, actual.size()); |
| } |
| |
| TEST_F(ServiceNetworkConfigTest, EmitChangeOnNetworkConfigChanges) { |
| service_->AttachNetwork(network_->AsWeakPtr()); |
| EXPECT_CALL(*GetAdaptor(), |
| EmitKeyValueStoreChanged(kNetworkConfigProperty, _)); |
| service_->network_event_handler()->OnIPConfigsPropertyUpdated( |
| /*interface_index=*/1); |
| } |
| |
| TEST_F(ServiceNetworkConfigTest, EmitChangeOnAttachNetwork) { |
| EXPECT_CALL(*GetAdaptor(), |
| EmitKeyValueStoreChanged(kNetworkConfigProperty, _)); |
| service_->AttachNetwork(network_->AsWeakPtr()); |
| } |
| |
| TEST_F(ServiceNetworkConfigTest, EmitChangeOnDetachNetwork) { |
| service_->AttachNetwork(network_->AsWeakPtr()); |
| EXPECT_CALL(*GetAdaptor(), |
| EmitKeyValueStoreChanged(kNetworkConfigProperty, _)); |
| service_->AttachNetwork(nullptr); |
| } |
| |
| } // namespace shill |