| // Copyright 2020 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "shill/dbus/client/client.h" |
| |
| #include <map> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include <chromeos/dbus/service_constants.h> |
| #include <dbus/mock_bus.h> |
| #include <dbus/mock_object_proxy.h> |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| #include <shill/dbus-proxy-mocks.h> |
| |
| using testing::_; |
| using testing::AnyNumber; |
| using testing::Invoke; |
| using testing::Return; |
| using testing::ReturnRef; |
| |
| using org::chromium::flimflam::DeviceProxyInterface; |
| using org::chromium::flimflam::DeviceProxyMock; |
| using org::chromium::flimflam::ManagerProxyInterface; |
| using org::chromium::flimflam::ManagerProxyMock; |
| using org::chromium::flimflam::ServiceProxyInterface; |
| using org::chromium::flimflam::ServiceProxyMock; |
| |
| namespace shill { |
| namespace { |
| |
| class FakeClient : public Client { |
| public: |
| explicit FakeClient(scoped_refptr<dbus::Bus> bus) |
| : Client(bus), |
| manager_mock_(new ManagerProxyMock), |
| service_mock_(new ServiceProxyMock) { |
| manager_proxy_.reset(manager_mock_); |
| default_service_proxy_.reset(service_mock_); |
| } |
| virtual ~FakeClient() = default; |
| |
| void NotifyOwnerChange(const std::string& old_owner, |
| const std::string& new_owner) { |
| OnOwnerChange(old_owner, new_owner); |
| } |
| |
| void NotifyManagerPropertyChange(const std::string& name, |
| const brillo::Any& value) { |
| OnManagerPropertyChange(name, value); |
| } |
| |
| void NotifyDefaultServicePropertyChange(const std::string& name, |
| const brillo::Any& value) { |
| OnDefaultServicePropertyChange(name, value); |
| } |
| |
| void NotifyDevicePropertyChange(const std::string& path, |
| const std::string& name, |
| const brillo::Any& value) { |
| OnDevicePropertyChange(false, path, name, value); |
| } |
| |
| void NotifyServicePropertyChange(const std::string& device_path, |
| const std::string& property_name, |
| const std::string& property_value) { |
| OnServicePropertyChange(device_path, property_name, property_value); |
| } |
| |
| ManagerProxyMock* manager() { return manager_mock_; } |
| ServiceProxyMock* default_service() { return service_mock_; } |
| dbus::ObjectPath default_service_path() { return service_path_; } |
| |
| DeviceProxyMock* PreMakeDevice(const dbus::ObjectPath device_path) { |
| auto* mock = new DeviceProxyMock(); |
| device_mocks_[device_path.value()] = mock; |
| // Set this default since this method will always be called in the |
| // DeviceWrapper destructor. |
| EXPECT_CALL(*mock, GetObjectPath).WillRepeatedly(ReturnRef(device_path)); |
| return mock; |
| } |
| |
| ServiceProxyMock* PreMakeService(const dbus::ObjectPath service_path) { |
| auto* mock = new ServiceProxyMock(); |
| service_mocks_[service_path.value()] = mock; |
| EXPECT_CALL(*mock, GetObjectPath).WillRepeatedly(ReturnRef(service_path)); |
| return mock; |
| } |
| |
| protected: |
| // Erase the default implementations so we can keep the same proxy pointers |
| // throughout. |
| void NewManagerProxy() override {} |
| void ReleaseManagerProxy() override {} |
| void NewDefaultServiceProxy(const dbus::ObjectPath& service_path) override { |
| service_path_ = service_path; |
| } |
| void ReleaseDefaultServiceProxy() override {} |
| |
| // Pass back the pre-allocated device which is necessary for correctly setting |
| // expectations in tests. Unfortunately this isn't going to let us re-add a |
| // device with the same path. |
| std::unique_ptr<DeviceProxyInterface> NewDeviceProxy( |
| const dbus::ObjectPath& device_path) override { |
| const auto it = device_mocks_.find(device_path.value()); |
| if (it == device_mocks_.end()) |
| return nullptr; |
| |
| std::unique_ptr<DeviceProxyMock> mock; |
| mock.reset(it->second); |
| return mock; |
| } |
| |
| std::unique_ptr<ServiceProxyInterface> NewServiceProxy( |
| const dbus::ObjectPath& service_path) override { |
| const auto it = service_mocks_.find(service_path.value()); |
| // Auto-premake for tests that don't need it. |
| ServiceProxyMock* ptr = (it == service_mocks_.end()) |
| ? PreMakeService(service_path) |
| : it->second; |
| |
| std::unique_ptr<ServiceProxyMock> mock; |
| mock.reset(ptr); |
| return mock; |
| } |
| |
| ManagerProxyMock* manager_mock_; |
| ServiceProxyMock* service_mock_; |
| dbus::ObjectPath service_path_; |
| std::map<std::string, DeviceProxyMock*> device_mocks_; |
| std::map<std::string, ServiceProxyMock*> service_mocks_; |
| }; |
| |
| class ClientTest : public testing::Test { |
| protected: |
| void SetUp() override { |
| default_service_changed_ = false; |
| default_device_connected_ = false; |
| default_device_ = {}; |
| devices_.clear(); |
| last_device_changed_.clear(); |
| last_device_cxn_state_ = Client::Device::ConnectionState::kUnknown; |
| |
| client_ = std::make_unique<FakeClient>(bus_mock_); |
| client_->RegisterDefaultServiceChangedHandler( |
| base::Bind(&ClientTest::DefaultServiceHandler, base::Unretained(this))); |
| client_->RegisterDefaultDeviceChangedHandler( |
| base::Bind(&ClientTest::DefaultDeviceHandler, base::Unretained(this))); |
| client_->RegisterDeviceAddedHandler( |
| base::Bind(&ClientTest::DeviceAddedHandler, base::Unretained(this))); |
| client_->RegisterDeviceRemovedHandler( |
| base::Bind(&ClientTest::DeviceRemovedHandler, base::Unretained(this))); |
| client_->RegisterDeviceChangedHandler( |
| base::Bind(&ClientTest::DeviceChangedHandler, base::Unretained(this))); |
| |
| // It's necessary to mock the base object used for the |
| // SetNameOwnerChangedCallback. |
| base_mock_ = new dbus::MockObjectProxy( |
| bus_mock_.get(), kFlimflamServiceName, dbus::ObjectPath("/")); |
| EXPECT_CALL(*bus_mock_, |
| GetObjectProxy(kFlimflamServiceName, dbus::ObjectPath("/"))) |
| .WillRepeatedly(Return(base_mock_.get())); |
| EXPECT_CALL(*base_mock_, SetNameOwnerChangedCallback(_)); |
| |
| // These are not blindly added - we expect the Client to issue these calls |
| // on the service in order to recover the path. |
| EXPECT_CALL(*client_->default_service(), GetObjectPath) |
| .WillRepeatedly( |
| Invoke([&]() { return client_->default_service_path(); })); |
| // This is the initial property registration called during Init(). |
| EXPECT_CALL(*client_->manager(), |
| DoRegisterPropertyChangedSignalHandler(_, _)); |
| client_->Init(); |
| } |
| |
| void DefaultServiceHandler(const std::string& type) { |
| default_service_type_ = type; |
| default_service_changed_ = true; |
| } |
| void DeviceAddedHandler(const Client::Device* const device) { |
| ASSERT_TRUE(device); |
| EXPECT_TRUE(devices_.find(device->ifname) == devices_.end()); |
| devices_.emplace(device->ifname, *device); |
| } |
| void DeviceRemovedHandler(const Client::Device* const device) { |
| ASSERT_TRUE(device); |
| EXPECT_TRUE(devices_.find(device->ifname) != devices_.end()); |
| devices_.erase(device->ifname); |
| } |
| void DefaultDeviceHandler(const Client::Device* const device) { |
| if (!device) { |
| default_device_connected_ = false; |
| default_device_ = {}; |
| return; |
| } |
| default_device_connected_ = true; |
| default_device_ = *device; |
| } |
| void DeviceChangedHandler(const Client::Device* const device) { |
| ASSERT_TRUE(device); |
| last_device_changed_ = device->ifname; |
| last_device_cxn_state_ = device->state; |
| } |
| |
| scoped_refptr<dbus::MockBus> bus_mock_{ |
| new dbus::MockBus{dbus::Bus::Options{}}}; |
| scoped_refptr<dbus::MockObjectProxy> base_mock_; |
| std::unique_ptr<FakeClient> client_; |
| |
| bool default_service_changed_; |
| std::string default_service_type_; |
| bool default_device_connected_; |
| Client::Device default_device_; |
| std::map<std::string, Client::Device> devices_; |
| std::string last_device_changed_; |
| Client::Device::ConnectionState last_device_cxn_state_; |
| }; |
| |
| ACTION_TEMPLATE(MovePointee, |
| HAS_1_TEMPLATE_PARAMS(int, k), |
| AND_1_VALUE_PARAMS(pointer)) { |
| *pointer = std::move(*(::std::get<k>(args))); |
| } |
| |
| TEST_F(ClientTest, ShillResetCreatesNewManagerProxy) { |
| EXPECT_CALL(*client_->manager(), |
| DoRegisterPropertyChangedSignalHandler(_, _)); |
| client_->NotifyOwnerChange("old", "new"); |
| } |
| |
| TEST_F(ClientTest, ShillLostDoesNotCreateNewManagerProxy) { |
| EXPECT_CALL(*client_->manager(), DoRegisterPropertyChangedSignalHandler(_, _)) |
| .Times(0); |
| client_->NotifyOwnerChange("old", ""); |
| } |
| |
| TEST_F(ClientTest, DefaultServiceHandlerCalledForValidServicePath) { |
| // When the default service changes, the client will start listening for |
| // property changes on that proxy. |
| dbus::ObjectProxy::OnConnectedCallback callback; |
| EXPECT_CALL(*client_->default_service(), |
| DoRegisterPropertyChangedSignalHandler(_, _)) |
| .WillOnce(MovePointee<1>(&callback)); |
| client_->NotifyManagerPropertyChange(kDefaultServiceProperty, |
| dbus::ObjectPath("/service/0")); |
| |
| brillo::VariantDictionary props; |
| props[kTypeProperty] = std::string("eth"); |
| EXPECT_CALL(*client_->default_service(), GetProperties(_, _, _)) |
| .WillOnce(DoAll(testing::SetArgPointee<0>(props), Return(true))); |
| |
| std::move(callback).Run(kFlimflamServiceName, kMonitorPropertyChanged, true); |
| EXPECT_TRUE(default_service_changed_); |
| EXPECT_EQ(default_service_type_, "eth"); |
| } |
| |
| TEST_F(ClientTest, DefaultServiceHandlerCalledForValidNoServicePath) { |
| // No registration should occur if there is no service. |
| client_->NotifyManagerPropertyChange(kDefaultServiceProperty, |
| dbus::ObjectPath("/")); |
| EXPECT_TRUE(default_service_changed_); |
| } |
| |
| TEST_F(ClientTest, DefaultServiceHandlerCalledForInvalidServicePath) { |
| // No registration should occur if there is no service. |
| client_->NotifyManagerPropertyChange(kDefaultServiceProperty, |
| dbus::ObjectPath()); |
| EXPECT_TRUE(default_service_changed_); |
| } |
| |
| TEST_F(ClientTest, DefaultDeviceDiscoveredOnNewService) { |
| // We want the device to exist first so the client doesn't run through the new |
| // device setup process when it detects the change. |
| const dbus::ObjectPath device_path("/device/eth0"); |
| auto* mock_device = client_->PreMakeDevice(device_path); |
| dbus::ObjectProxy::OnConnectedCallback device_callback; |
| EXPECT_CALL(*mock_device, DoRegisterPropertyChangedSignalHandler(_, _)) |
| .WillOnce(MovePointee<1>(&device_callback)); |
| |
| client_->NotifyManagerPropertyChange( |
| kDevicesProperty, std::vector<dbus::ObjectPath>({device_path})); |
| |
| brillo::VariantDictionary props; |
| props[kTypeProperty] = std::string(kTypeEthernet); |
| props[kInterfaceProperty] = std::string("eth0"); |
| EXPECT_CALL(*mock_device, GetProperties(_, _, _)) |
| .WillOnce(DoAll(testing::SetArgPointee<0>(props), Return(true))); |
| |
| // Manually trigger the registration callback since that doesn't happen in the |
| // mocks. |
| std::move(device_callback) |
| .Run(kFlimflamServiceName, kMonitorPropertyChanged, true); |
| |
| // This configures the default service properties and fires the initial |
| // callback that is normally run after the proxy registers. |
| const dbus::ObjectPath service_path("/service/0"); |
| auto* mock_service = client_->default_service(); |
| dbus::ObjectProxy::OnConnectedCallback service_callback; |
| EXPECT_CALL(*mock_service, DoRegisterPropertyChangedSignalHandler(_, _)) |
| .WillOnce(MovePointee<1>(&service_callback)); |
| |
| client_->NotifyManagerPropertyChange(kDefaultServiceProperty, service_path); |
| |
| brillo::VariantDictionary service_props; |
| service_props[kIsConnectedProperty] = true; |
| service_props[kDeviceProperty] = device_path; |
| EXPECT_CALL(*mock_service, GetProperties(_, _, _)) |
| .WillOnce(DoAll(testing::SetArgPointee<0>(service_props), Return(true))); |
| |
| EXPECT_FALSE(default_device_connected_); |
| EXPECT_EQ(default_device_.ifname, ""); |
| |
| std::move(service_callback) |
| .Run(kFlimflamServiceName, kMonitorPropertyChanged, true); |
| |
| EXPECT_TRUE(default_device_connected_); |
| EXPECT_EQ(default_device_.ifname, "eth0"); |
| } |
| |
| TEST_F(ClientTest, DefaultServiceConnectionChangeCallsDefaultHandler) { |
| // We want the device to exist first so the client doesn't run through the new |
| // device setup process when it detects the change. |
| const dbus::ObjectPath device_path("/device/eth0"); |
| auto* mock_device = client_->PreMakeDevice(device_path); |
| dbus::ObjectProxy::OnConnectedCallback device_callback; |
| EXPECT_CALL(*mock_device, DoRegisterPropertyChangedSignalHandler(_, _)) |
| .WillOnce(MovePointee<1>(&device_callback)); |
| |
| client_->NotifyManagerPropertyChange( |
| kDevicesProperty, std::vector<dbus::ObjectPath>({device_path})); |
| |
| brillo::VariantDictionary props; |
| props[kTypeProperty] = std::string(kTypeEthernet); |
| props[kInterfaceProperty] = std::string("eth0"); |
| EXPECT_CALL(*mock_device, GetProperties(_, _, _)) |
| .WillOnce(DoAll(testing::SetArgPointee<0>(props), Return(true))); |
| |
| // Manually trigger the registration callback since that doesn't happen in the |
| // mocks. |
| std::move(device_callback) |
| .Run(kFlimflamServiceName, kMonitorPropertyChanged, true); |
| |
| // Set the default device. |
| client_->NotifyDefaultServicePropertyChange(kDeviceProperty, device_path); |
| |
| // Trigger the change handler. |
| EXPECT_FALSE(default_device_connected_); |
| client_->NotifyDefaultServicePropertyChange(kIsConnectedProperty, true); |
| EXPECT_TRUE(default_device_connected_); |
| client_->NotifyDefaultServicePropertyChange(kIsConnectedProperty, false); |
| EXPECT_FALSE(default_device_connected_); |
| client_->NotifyDefaultServicePropertyChange(kIsConnectedProperty, true); |
| EXPECT_TRUE(default_device_connected_); |
| } |
| |
| TEST_F(ClientTest, DefaultServiceDeviceChangeCallsDefaultHandler) { |
| // Add the devices. |
| const dbus::ObjectPath eth0_path("/device/eth0"), wlan0_path("/device/wlan0"), |
| eth0_service_path("service/0"), wlan0_service_path("/service/1"); |
| auto* eth0_device = client_->PreMakeDevice(eth0_path); |
| auto* wlan0_device = client_->PreMakeDevice(wlan0_path); |
| dbus::ObjectProxy::OnConnectedCallback eth0_callback, wlan0_callback; |
| EXPECT_CALL(*eth0_device, DoRegisterPropertyChangedSignalHandler(_, _)) |
| .WillOnce(MovePointee<1>(ð0_callback)); |
| EXPECT_CALL(*wlan0_device, DoRegisterPropertyChangedSignalHandler(_, _)) |
| .WillOnce(MovePointee<1>(&wlan0_callback)); |
| |
| client_->NotifyManagerPropertyChange( |
| kDevicesProperty, std::vector<dbus::ObjectPath>({eth0_path, wlan0_path})); |
| |
| brillo::VariantDictionary eth0_props, wlan0_props; |
| eth0_props[kTypeProperty] = std::string(kTypeEthernet); |
| eth0_props[kInterfaceProperty] = std::string("eth0"); |
| eth0_props[kSelectedServiceProperty] = eth0_service_path; |
| EXPECT_CALL(*eth0_device, GetProperties(_, _, _)) |
| .WillOnce(DoAll(testing::SetArgPointee<0>(eth0_props), Return(true))); |
| wlan0_props[kTypeProperty] = std::string(kTypeWifi); |
| wlan0_props[kInterfaceProperty] = std::string("wlan0"); |
| wlan0_props[kSelectedServiceProperty] = wlan0_service_path; |
| EXPECT_CALL(*wlan0_device, GetProperties(_, _, _)) |
| .WillOnce(DoAll(testing::SetArgPointee<0>(wlan0_props), Return(true))); |
| |
| std::move(eth0_callback) |
| .Run(kFlimflamServiceName, kMonitorPropertyChanged, true); |
| std::move(wlan0_callback) |
| .Run(kFlimflamServiceName, kMonitorPropertyChanged, true); |
| |
| // Set up initial state. |
| client_->NotifyDefaultServicePropertyChange(kDeviceProperty, eth0_path); |
| client_->NotifyDefaultServicePropertyChange(kIsConnectedProperty, true); |
| |
| EXPECT_TRUE(default_device_connected_); |
| EXPECT_EQ(default_device_.ifname, "eth0"); |
| |
| // Now trigger the default device change to wifi. |
| client_->NotifyDefaultServicePropertyChange(kDeviceProperty, wlan0_path); |
| |
| EXPECT_TRUE(default_device_connected_); |
| EXPECT_EQ(default_device_.ifname, "wlan0"); |
| } |
| |
| TEST_F(ClientTest, DefaultServiceChangeAddsDefaultDeviceIfMissing) { |
| // Normally, all physical devices are already known so when one becomes the |
| // default it will only trigger the default device callback. But VPN devices |
| // are (intentionally) not provided by shill and are therefore not tracked. |
| // But this client will add VPN devices if they become the device associated |
| // with the default service. |
| // Note that this also verifies that if for some reason (odd-ball |
| // synchronization) when the default device is discover, if it happens be to |
| // unknown at the time, it will be added and tracked. |
| const dbus::ObjectPath device_path("/device/ppp0"); |
| auto* mock_device = client_->PreMakeDevice(device_path); |
| dbus::ObjectProxy::OnConnectedCallback device_callback; |
| EXPECT_CALL(*mock_device, DoRegisterPropertyChangedSignalHandler(_, _)) |
| .WillOnce(MovePointee<1>(&device_callback)); |
| |
| brillo::VariantDictionary device_props; |
| device_props[kTypeProperty] = std::string(kTypePPP); |
| device_props[kInterfaceProperty] = std::string("ppp0"); |
| EXPECT_CALL(*mock_device, GetProperties(_, _, _)) |
| .WillOnce(DoAll(testing::SetArgPointee<0>(device_props), Return(true))); |
| |
| // Trigger the state change. |
| client_->NotifyDefaultServicePropertyChange(kIsConnectedProperty, true); |
| client_->NotifyDefaultServicePropertyChange(kDeviceProperty, device_path); |
| |
| std::move(device_callback) |
| .Run(kFlimflamServiceName, kMonitorPropertyChanged, true); |
| |
| EXPECT_EQ(devices_.size(), 1); |
| EXPECT_TRUE(devices_.find("ppp0") != devices_.end()); |
| |
| // This change should have also invoked the default device handler as well. |
| EXPECT_TRUE(default_device_connected_); |
| EXPECT_EQ(default_device_.ifname, "ppp0"); |
| } |
| |
| TEST_F(ClientTest, DeviceAddedHandlerCalledOncePerNewDevice) { |
| const dbus::ObjectPath device_path("/device/eth0"); |
| auto* mock_device = client_->PreMakeDevice(device_path); |
| dbus::ObjectProxy::OnConnectedCallback callback; |
| EXPECT_CALL(*mock_device, DoRegisterPropertyChangedSignalHandler(_, _)) |
| .WillOnce(MovePointee<1>(&callback)); |
| |
| client_->NotifyManagerPropertyChange( |
| kDevicesProperty, std::vector<dbus::ObjectPath>({device_path})); |
| |
| brillo::VariantDictionary props; |
| props[kTypeProperty] = std::string(kTypeEthernet); |
| props[kInterfaceProperty] = std::string("eth0"); |
| EXPECT_CALL(*mock_device, GetProperties(_, _, _)) |
| .WillOnce(DoAll(testing::SetArgPointee<0>(props), Return(true))); |
| |
| // Manually trigger the registration callback since that doesn't happen in the |
| // mocks. |
| std::move(callback).Run(kFlimflamServiceName, kMonitorPropertyChanged, true); |
| EXPECT_EQ(devices_.size(), 1); |
| EXPECT_TRUE(devices_.find("eth0") != devices_.end()); |
| |
| // Attempt to add the same device, this should not trigger the creation of a |
| // new proxy or property registration and the rest. If this logic fails and |
| // the device is readded, the above expectation(s) will be oversaturated and |
| // fail. |
| devices_.clear(); |
| client_->NotifyManagerPropertyChange( |
| kDevicesProperty, std::vector<dbus::ObjectPath>({device_path})); |
| EXPECT_TRUE(devices_.empty()); |
| } |
| |
| TEST_F(ClientTest, DeviceAddedHandlerNotCalledIfInterfaceMissing) { |
| const dbus::ObjectPath device_path("/device/eth0"); |
| auto* mock_device = client_->PreMakeDevice(device_path); |
| dbus::ObjectProxy::OnConnectedCallback callback; |
| EXPECT_CALL(*mock_device, DoRegisterPropertyChangedSignalHandler(_, _)) |
| .WillOnce(MovePointee<1>(&callback)); |
| |
| client_->NotifyManagerPropertyChange( |
| kDevicesProperty, std::vector<dbus::ObjectPath>({device_path})); |
| |
| brillo::VariantDictionary props; |
| props[kTypeProperty] = std::string(kTypeEthernet); |
| EXPECT_CALL(*mock_device, GetProperties(_, _, _)) |
| .WillOnce(DoAll(testing::SetArgPointee<0>(props), Return(true))); |
| |
| // Manually trigger the registration callback since that doesn't happen in the |
| // mocks. |
| std::move(callback).Run(kFlimflamServiceName, kMonitorPropertyChanged, true); |
| EXPECT_TRUE(devices_.empty()); |
| } |
| |
| TEST_F(ClientTest, DeviceRemovedHandlerCalled) { |
| // Add the device. |
| const dbus::ObjectPath device_path("/device/eth0"); |
| auto* mock_device = client_->PreMakeDevice(device_path); |
| dbus::ObjectProxy::OnConnectedCallback callback; |
| EXPECT_CALL(*mock_device, DoRegisterPropertyChangedSignalHandler(_, _)) |
| .WillOnce(MovePointee<1>(&callback)); |
| |
| client_->NotifyManagerPropertyChange( |
| kDevicesProperty, std::vector<dbus::ObjectPath>({device_path})); |
| |
| brillo::VariantDictionary props; |
| props[kTypeProperty] = std::string(kTypeEthernet); |
| props[kInterfaceProperty] = std::string("eth0"); |
| EXPECT_CALL(*mock_device, GetProperties(_, _, _)) |
| .WillOnce(DoAll(testing::SetArgPointee<0>(props), Return(true))); |
| |
| // Manually trigger the registration callback since that doesn't happen in the |
| // mocks. |
| std::move(callback).Run(kFlimflamServiceName, kMonitorPropertyChanged, true); |
| EXPECT_EQ(devices_.size(), 1); |
| EXPECT_TRUE(devices_.find("eth0") != devices_.end()); |
| |
| // Remove the device by updating the device list. |
| client_->NotifyManagerPropertyChange(kDevicesProperty, |
| std::vector<dbus::ObjectPath>({})); |
| EXPECT_TRUE(devices_.empty()); |
| } |
| |
| TEST_F(ClientTest, DeviceHandlersCalledOnIPConfigChange) { |
| // Set up 2 devices here to ensure the changes are captured correctly. |
| const dbus::ObjectPath eth0_path("/device/eth0"), wlan0_path("/device/wlan0"), |
| eth0_service_path("service/0"), wlan0_service_path("/service/1"); |
| auto* eth0_device = client_->PreMakeDevice(eth0_path); |
| auto* wlan0_device = client_->PreMakeDevice(wlan0_path); |
| dbus::ObjectProxy::OnConnectedCallback eth0_callback, wlan0_callback; |
| EXPECT_CALL(*eth0_device, DoRegisterPropertyChangedSignalHandler(_, _)) |
| .WillOnce(MovePointee<1>(ð0_callback)); |
| EXPECT_CALL(*wlan0_device, DoRegisterPropertyChangedSignalHandler(_, _)) |
| .WillOnce(MovePointee<1>(&wlan0_callback)); |
| |
| client_->NotifyManagerPropertyChange( |
| kDevicesProperty, std::vector<dbus::ObjectPath>({eth0_path, wlan0_path})); |
| |
| brillo::VariantDictionary eth0_props, wlan0_props; |
| eth0_props[kTypeProperty] = std::string(kTypeEthernet); |
| eth0_props[kInterfaceProperty] = std::string("eth0"); |
| eth0_props[kSelectedServiceProperty] = eth0_service_path; |
| EXPECT_CALL(*eth0_device, GetProperties(_, _, _)) |
| .WillOnce(DoAll(testing::SetArgPointee<0>(eth0_props), Return(true))); |
| wlan0_props[kTypeProperty] = std::string(kTypeWifi); |
| wlan0_props[kInterfaceProperty] = std::string("wlan0"); |
| wlan0_props[kSelectedServiceProperty] = wlan0_service_path; |
| EXPECT_CALL(*wlan0_device, GetProperties(_, _, _)) |
| .WillOnce(DoAll(testing::SetArgPointee<0>(wlan0_props), Return(true))); |
| |
| std::move(eth0_callback) |
| .Run(kFlimflamServiceName, kMonitorPropertyChanged, true); |
| std::move(wlan0_callback) |
| .Run(kFlimflamServiceName, kMonitorPropertyChanged, true); |
| |
| // Set up initial state. |
| client_->NotifyDefaultServicePropertyChange(kDeviceProperty, eth0_path); |
| client_->NotifyDefaultServicePropertyChange(kIsConnectedProperty, true); |
| |
| // This test doesn't worry about parsing IPConfigs, only that the right |
| // handler is called, so it doesn't matter what we send through here. |
| last_device_changed_.clear(); |
| // First check the non-default device. |
| client_->NotifyDevicePropertyChange("/device/wlan0", kIPConfigsProperty, |
| brillo::Any()); |
| EXPECT_EQ(last_device_changed_, "wlan0"); |
| // Now the default. We're also going to verify the default device handler is |
| // called next, so clear that state first as well. |
| default_device_connected_ = false; |
| default_device_ = {}; |
| client_->NotifyDevicePropertyChange("/device/eth0", kIPConfigsProperty, |
| brillo::Any()); |
| EXPECT_EQ(last_device_changed_, "eth0"); |
| EXPECT_TRUE(default_device_connected_); |
| EXPECT_EQ(default_device_.ifname, "eth0"); |
| } |
| |
| TEST_F(ClientTest, DeviceSelectedServiceConnectStateObtained) { |
| const dbus::ObjectPath device_path("/device/eth0"), |
| service_path("/service/1"); |
| auto* mock_device = client_->PreMakeDevice(device_path); |
| auto* mock_service = client_->PreMakeService(service_path); |
| dbus::ObjectProxy::OnConnectedCallback device_callback, service_callback; |
| EXPECT_CALL(*mock_device, DoRegisterPropertyChangedSignalHandler(_, _)) |
| .WillOnce(MovePointee<1>(&device_callback)); |
| EXPECT_CALL(*mock_service, DoRegisterPropertyChangedSignalHandler(_, _)) |
| .WillOnce(MovePointee<1>(&service_callback)); |
| |
| client_->NotifyManagerPropertyChange( |
| kDevicesProperty, std::vector<dbus::ObjectPath>({device_path})); |
| |
| brillo::VariantDictionary device_props; |
| device_props[kTypeProperty] = std::string(kTypeEthernet); |
| device_props[kInterfaceProperty] = std::string("eth0"); |
| device_props[kSelectedServiceProperty] = service_path; |
| EXPECT_CALL(*mock_device, GetProperties(_, _, _)) |
| .WillOnce(DoAll(testing::SetArgPointee<0>(device_props), Return(true))); |
| |
| brillo::VariantDictionary service_props; |
| service_props[kStateProperty] = std::string(kStateOnline); |
| EXPECT_CALL(*mock_service, GetProperties(_, _, _)) |
| .WillRepeatedly( |
| DoAll(testing::SetArgPointee<0>(service_props), Return(true))); |
| |
| // Manually trigger the registration callback since that doesn't happen in the |
| // mocks. |
| std::move(device_callback) |
| .Run(kFlimflamServiceName, kMonitorPropertyChanged, true); |
| std::move(service_callback) |
| .Run(kFlimflamServiceName, kMonitorPropertyChanged, true); |
| EXPECT_EQ(devices_.size(), 1); |
| const auto it = devices_.find("eth0"); |
| EXPECT_NE(it, devices_.end()); |
| EXPECT_EQ(it->second.state, Client::Device::ConnectionState::kOnline); |
| } |
| |
| TEST_F(ClientTest, DeviceHandlersCalledOnSelectedServiceStateChange) { |
| // Set up 2 devices here to ensure the changes are captured correctly. |
| const dbus::ObjectPath eth0_path("/device/eth0"), wlan0_path("/device/wlan0"), |
| eth0_service_path("service/0"), wlan0_service_path("/service/1"); |
| auto* eth0_device = client_->PreMakeDevice(eth0_path); |
| auto* wlan0_device = client_->PreMakeDevice(wlan0_path); |
| auto* eth0_service = client_->PreMakeService(eth0_service_path); |
| auto* wlan0_service = client_->PreMakeService(wlan0_service_path); |
| dbus::ObjectProxy::OnConnectedCallback eth0_callback, wlan0_callback, |
| eth0_service_callback, wlan0_service_callback; |
| EXPECT_CALL(*eth0_device, DoRegisterPropertyChangedSignalHandler(_, _)) |
| .WillOnce(MovePointee<1>(ð0_callback)); |
| EXPECT_CALL(*wlan0_device, DoRegisterPropertyChangedSignalHandler(_, _)) |
| .WillOnce(MovePointee<1>(&wlan0_callback)); |
| EXPECT_CALL(*eth0_service, DoRegisterPropertyChangedSignalHandler(_, _)) |
| .WillOnce(MovePointee<1>(ð0_service_callback)); |
| EXPECT_CALL(*wlan0_service, DoRegisterPropertyChangedSignalHandler(_, _)) |
| .WillOnce(MovePointee<1>(&wlan0_service_callback)); |
| |
| client_->NotifyManagerPropertyChange( |
| kDevicesProperty, std::vector<dbus::ObjectPath>({eth0_path, wlan0_path})); |
| |
| brillo::VariantDictionary eth0_props, wlan0_props; |
| eth0_props[kTypeProperty] = std::string(kTypeEthernet); |
| eth0_props[kInterfaceProperty] = std::string("eth0"); |
| eth0_props[kSelectedServiceProperty] = eth0_service_path; |
| EXPECT_CALL(*eth0_device, GetProperties(_, _, _)) |
| .WillOnce(DoAll(testing::SetArgPointee<0>(eth0_props), Return(true))); |
| wlan0_props[kTypeProperty] = std::string(kTypeWifi); |
| wlan0_props[kInterfaceProperty] = std::string("wlan0"); |
| wlan0_props[kSelectedServiceProperty] = wlan0_service_path; |
| EXPECT_CALL(*wlan0_device, GetProperties(_, _, _)) |
| .WillOnce(DoAll(testing::SetArgPointee<0>(wlan0_props), Return(true))); |
| |
| brillo::VariantDictionary service_props; |
| service_props[kStateProperty] = std::string(kStateOnline); |
| EXPECT_CALL(*eth0_service, GetProperties(_, _, _)) |
| .WillRepeatedly( |
| DoAll(testing::SetArgPointee<0>(service_props), Return(true))); |
| EXPECT_CALL(*wlan0_service, GetProperties(_, _, _)) |
| .WillRepeatedly( |
| DoAll(testing::SetArgPointee<0>(service_props), Return(true))); |
| |
| std::move(eth0_callback) |
| .Run(kFlimflamServiceName, kMonitorPropertyChanged, true); |
| std::move(wlan0_callback) |
| .Run(kFlimflamServiceName, kMonitorPropertyChanged, true); |
| std::move(eth0_service_callback) |
| .Run(kFlimflamServiceName, kMonitorPropertyChanged, true); |
| std::move(wlan0_service_callback) |
| .Run(kFlimflamServiceName, kMonitorPropertyChanged, true); |
| |
| // Set up initial state. |
| client_->NotifyDefaultServicePropertyChange(kDeviceProperty, eth0_path); |
| client_->NotifyDefaultServicePropertyChange(kIsConnectedProperty, true); |
| |
| last_device_changed_.clear(); |
| // First check the non-default device. |
| client_->NotifyServicePropertyChange("/device/wlan0", kStateProperty, |
| kStateFailure); |
| EXPECT_EQ(last_device_changed_, "wlan0"); |
| EXPECT_EQ(last_device_cxn_state_, Client::Device::ConnectionState::kFailure); |
| |
| // Now the default. We're also going to verify the default device handler is |
| // called next, so clear that state first as well. |
| default_device_ = {}; |
| client_->NotifyServicePropertyChange("/device/eth0", kStateProperty, |
| kStateReady); |
| EXPECT_EQ(last_device_changed_, "eth0"); |
| EXPECT_EQ(last_device_cxn_state_, Client::Device::ConnectionState::kReady); |
| EXPECT_EQ(default_device_.state, Client::Device::ConnectionState::kReady); |
| EXPECT_EQ(default_device_.ifname, "eth0"); |
| } |
| |
| TEST_F(ClientTest, DeviceHandlersCalledOnSelectedServiceChange) { |
| const dbus::ObjectPath wlan0_path("/device/wlan0"), |
| service0_path("service/0"), service1_path("/service/1"); |
| auto* wlan0_device = client_->PreMakeDevice(wlan0_path); |
| auto* service0 = client_->PreMakeService(service0_path); |
| auto* service1 = client_->PreMakeService(service1_path); |
| dbus::ObjectProxy::OnConnectedCallback wlan0_callback, service0_callback, |
| service1_callback; |
| EXPECT_CALL(*wlan0_device, DoRegisterPropertyChangedSignalHandler(_, _)) |
| .WillOnce(MovePointee<1>(&wlan0_callback)); |
| EXPECT_CALL(*service0, DoRegisterPropertyChangedSignalHandler(_, _)) |
| .WillOnce(MovePointee<1>(&service0_callback)); |
| EXPECT_CALL(*service1, DoRegisterPropertyChangedSignalHandler(_, _)) |
| .WillOnce(MovePointee<1>(&service1_callback)); |
| |
| client_->NotifyManagerPropertyChange( |
| kDevicesProperty, std::vector<dbus::ObjectPath>({wlan0_path})); |
| |
| brillo::VariantDictionary wlan0_props; |
| wlan0_props[kTypeProperty] = std::string(kTypeWifi); |
| wlan0_props[kInterfaceProperty] = std::string("wlan0"); |
| wlan0_props[kSelectedServiceProperty] = service0_path; |
| EXPECT_CALL(*wlan0_device, GetProperties(_, _, _)) |
| .WillOnce(DoAll(testing::SetArgPointee<0>(wlan0_props), Return(true))); |
| |
| brillo::VariantDictionary service_props; |
| service_props[kStateProperty] = std::string(kStateOnline); |
| EXPECT_CALL(*service0, GetProperties(_, _, _)) |
| .WillRepeatedly( |
| DoAll(testing::SetArgPointee<0>(service_props), Return(true))); |
| EXPECT_CALL(*service1, GetProperties(_, _, _)) |
| .WillRepeatedly( |
| DoAll(testing::SetArgPointee<0>(service_props), Return(true))); |
| |
| std::move(wlan0_callback) |
| .Run(kFlimflamServiceName, kMonitorPropertyChanged, true); |
| std::move(service0_callback) |
| .Run(kFlimflamServiceName, kMonitorPropertyChanged, true); |
| // Set up initial state. |
| client_->NotifyDefaultServicePropertyChange(kDeviceProperty, wlan0_path); |
| client_->NotifyDefaultServicePropertyChange(kIsConnectedProperty, true); |
| |
| last_device_changed_.clear(); |
| client_->NotifyDevicePropertyChange("/device/wlan0", kSelectedServiceProperty, |
| service1_path); |
| std::move(service1_callback) |
| .Run(kFlimflamServiceName, kMonitorPropertyChanged, true); |
| |
| EXPECT_EQ(last_device_changed_, "wlan0"); |
| } |
| |
| } // namespace |
| } // namespace shill |