diff options
Diffstat (limited to 'device_info_unittest.cc')
-rw-r--r-- | device_info_unittest.cc | 373 |
1 files changed, 373 insertions, 0 deletions
diff --git a/device_info_unittest.cc b/device_info_unittest.cc new file mode 100644 index 0000000..afe59ec --- /dev/null +++ b/device_info_unittest.cc @@ -0,0 +1,373 @@ +// Copyright 2014 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 "apmanager/device_info.h" + +#include <linux/netlink.h> +#include <linux/rtnetlink.h> + +#include <map> +#include <string> +#include <vector> + +#include <base/files/file_util.h> +#include <base/files/scoped_temp_dir.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <shill/net/byte_string.h> +#include <shill/net/mock_netlink_manager.h> +#include "shill/net/netlink_message_matchers.h" +#include "shill/net/nl80211_attribute.h" +#include "shill/net/nl80211_message.h" +#include <shill/net/rtnl_message.h> + +#include "apmanager/mock_device.h" +#include "apmanager/mock_manager.h" + +using shill::ByteString; +using shill::Nl80211Message; +using shill::RTNLMessage; +using std::map; +using std::string; +using std::vector; +using ::testing::_; +using ::testing::Mock; + +namespace apmanager { + +namespace { + +const char kTestDeviceName[] = "test-phy"; +const char kTestInterface0Name[] = "test-interface0"; +const char kTestInterface1Name[] = "test-interface1"; +const uint32_t kTestInterface0Index = 1000; +const uint32_t kTestInterface1Index = 1001; + +} // namespace + +class DeviceInfoTest : public testing::Test { + public: + DeviceInfoTest() : device_info_(&manager_) {} + virtual ~DeviceInfoTest() {} + + virtual void SetUp() { + // Setup temporary directory for device info files. + CHECK(temp_dir_.CreateUniqueTempDir()); + device_info_root_ = temp_dir_.path().Append("sys/class/net"); + device_info_.device_info_root_ = device_info_root_; + + // Setup mock pointers; + device_info_.netlink_manager_ = &netlink_manager_; + } + + bool IsWifiInterface(const string& interface_name) { + return device_info_.IsWifiInterface(interface_name); + } + + void CreateDeviceInfoFile(const string& interface_name, + const string& file_name, + const string& contents) { + base::FilePath info_path = + device_info_root_.Append(interface_name).Append(file_name); + EXPECT_TRUE(base::CreateDirectory(info_path.DirName())); + EXPECT_TRUE(base::WriteFile(info_path, contents.c_str(), contents.size())); + } + + void SendLinkMsg(RTNLMessage::Mode mode, + uint32_t interface_index, + const string& interface_name) { + RTNLMessage message(RTNLMessage::kTypeLink, + mode, + 0, + 0, + 0, + interface_index, + shill::IPAddress::kFamilyIPv4); + message.SetAttribute(static_cast<uint16_t>(IFLA_IFNAME), + ByteString(interface_name, true)); + device_info_.LinkMsgHandler(message); + } + + void VerifyInterfaceList(const vector<Device::WiFiInterface>& interfaces) { + // Verify number of elements in the interface infos map and interface index + // of the elements in the map. + EXPECT_EQ(interfaces.size(), device_info_.interface_infos_.size()); + for (const auto& interface : interfaces) { + map<uint32_t, Device::WiFiInterface>::iterator it = + device_info_.interface_infos_.find(interface.iface_index); + EXPECT_NE(device_info_.interface_infos_.end(), it); + EXPECT_TRUE(interface.Equals(it->second)); + } + } + + void VerifyDeviceList(const vector<scoped_refptr<Device>>& devices) { + // Verify number of elements in the device map and the elements in the map. + EXPECT_EQ(devices.size(), device_info_.devices_.size()); + for (const auto& device : devices) { + map<string, scoped_refptr<Device>>::iterator it = + device_info_.devices_.find(device->GetDeviceName()); + EXPECT_NE(device_info_.devices_.end(), it); + EXPECT_EQ(device, it->second); + } + } + void AddInterface(const Device::WiFiInterface& interface) { + device_info_.interface_infos_[interface.iface_index] = interface; + } + + void OnWiFiPhyInfoReceived(const Nl80211Message& message) { + device_info_.OnWiFiPhyInfoReceived(message); + } + + void OnWiFiInterfaceInfoReceived(const Nl80211Message& message) { + device_info_.OnWiFiInterfaceInfoReceived(message); + } + + void OnWiFiInterfacePhyInfoReceived(uint32_t interface_index, + const Nl80211Message& message) { + device_info_.OnWiFiInterfacePhyInfoReceived(interface_index, message); + } + + void RegisterDevice(scoped_refptr<Device> device) { + device_info_.RegisterDevice(device); + } + + protected: + DeviceInfo device_info_; + MockManager manager_; + shill::MockNetlinkManager netlink_manager_; + base::ScopedTempDir temp_dir_; + base::FilePath device_info_root_; +}; + +MATCHER_P2(IsGetInfoMessage, command, index, "") { + if (arg->message_type() != Nl80211Message::GetMessageType()) { + return false; + } + const Nl80211Message *msg = reinterpret_cast<const Nl80211Message *>(arg); + if (msg->command() != command) { + return false; + } + uint32_t interface_index; + if (!msg->const_attributes()->GetU32AttributeValue(NL80211_ATTR_IFINDEX, + &interface_index)) { + return false; + } + // kInterfaceIndex is signed, but the attribute as handed from the kernel + // is unsigned. We're silently casting it away with this assignment. + uint32_t test_interface_index = index; + return interface_index == test_interface_index; +} + +MATCHER_P(IsInterface, interface, "") { + return arg.Equals(interface); +} + +MATCHER_P(IsDevice, device_name, "") { + return arg->GetDeviceName() == device_name; +} + +TEST_F(DeviceInfoTest, EnumerateDevices) { + shill::NewWiphyMessage message; + + // No device name in the message, failed to create device. + EXPECT_CALL(manager_, RegisterDevice(_)).Times(0); + OnWiFiPhyInfoReceived(message); + + // Device name in the message, device should be created/register to manager. + message.attributes()->CreateNl80211Attribute( + NL80211_ATTR_WIPHY_NAME, shill::NetlinkMessage::MessageContext()); + message.attributes()->SetStringAttributeValue(NL80211_ATTR_WIPHY_NAME, + kTestDeviceName); + EXPECT_CALL(manager_, RegisterDevice(IsDevice(kTestDeviceName))).Times(1); + OnWiFiPhyInfoReceived(message); + Mock::VerifyAndClearExpectations(&manager_); + + // Receive a message for a device already created, should not create/register + // device again. + EXPECT_CALL(manager_, RegisterDevice(_)).Times(0); + OnWiFiPhyInfoReceived(message); +} + +TEST_F(DeviceInfoTest, IsWiFiInterface) { + // No device info file exist, not a wifi interface. + EXPECT_FALSE(IsWifiInterface(kTestInterface0Name)); + + // Device info for an ethernet device, not a wifi interface + CreateDeviceInfoFile(kTestInterface0Name, "uevent", "INTERFACE=eth0\n"); + EXPECT_FALSE(IsWifiInterface(kTestInterface0Name)); + + // Device info for a wifi interface. + CreateDeviceInfoFile(kTestInterface1Name, "uevent", "DEVTYPE=wlan\n"); + EXPECT_TRUE(IsWifiInterface(kTestInterface1Name)); +} + +TEST_F(DeviceInfoTest, InterfaceDetection) { + vector<Device::WiFiInterface> interface_list; + // Ignore non-wifi interface. + SendLinkMsg(RTNLMessage::kModeAdd, + kTestInterface0Index, + kTestInterface0Name); + VerifyInterfaceList(interface_list); + + // AddLink event for wifi interface. + CreateDeviceInfoFile(kTestInterface0Name, "uevent", "DEVTYPE=wlan\n"); + EXPECT_CALL(netlink_manager_, SendNl80211Message( + IsGetInfoMessage(NL80211_CMD_GET_INTERFACE, kTestInterface0Index), + _, _, _)).Times(1); + SendLinkMsg(RTNLMessage::kModeAdd, + kTestInterface0Index, + kTestInterface0Name); + interface_list.push_back(Device::WiFiInterface( + kTestInterface0Name, "", kTestInterface0Index, 0)); + VerifyInterfaceList(interface_list); + Mock::VerifyAndClearExpectations(&netlink_manager_); + + // AddLink event for another wifi interface. + CreateDeviceInfoFile(kTestInterface1Name, "uevent", "DEVTYPE=wlan\n"); + EXPECT_CALL(netlink_manager_, SendNl80211Message( + IsGetInfoMessage(NL80211_CMD_GET_INTERFACE, kTestInterface1Index), + _, _, _)).Times(1); + SendLinkMsg(RTNLMessage::kModeAdd, + kTestInterface1Index, + kTestInterface1Name); + interface_list.push_back(Device::WiFiInterface( + kTestInterface1Name, "", kTestInterface1Index, 0)); + VerifyInterfaceList(interface_list); + Mock::VerifyAndClearExpectations(&netlink_manager_); + + // AddLink event for an interface that's already added, no change to interface + // list. + EXPECT_CALL(netlink_manager_, SendNl80211Message(_, _, _, _)).Times(0); + SendLinkMsg(RTNLMessage::kModeAdd, + kTestInterface0Index, + kTestInterface0Name); + VerifyInterfaceList(interface_list); + Mock::VerifyAndClearExpectations(&netlink_manager_); + + // Remove the first wifi interface. + SendLinkMsg(RTNLMessage::kModeDelete, + kTestInterface0Index, + kTestInterface0Name); + interface_list.clear(); + interface_list.push_back(Device::WiFiInterface( + kTestInterface1Name, "", kTestInterface1Index, 0)); + VerifyInterfaceList(interface_list); + + // Remove the non-exist interface, no change to the list. + SendLinkMsg(RTNLMessage::kModeDelete, + kTestInterface0Index, + kTestInterface0Name); + VerifyInterfaceList(interface_list); + + // Remove the last interface, list should be empty now. + SendLinkMsg(RTNLMessage::kModeDelete, + kTestInterface1Index, + kTestInterface1Name); + interface_list.clear(); + VerifyInterfaceList(interface_list); +} + +TEST_F(DeviceInfoTest, ParseWifiInterfaceInfo) { + // Add an interface without interface type info. + Device::WiFiInterface interface( + kTestInterface0Name, "", kTestInterface0Index, 0); + AddInterface(interface); + vector<Device::WiFiInterface> interface_list; + interface_list.push_back(interface); + + // Message contain no interface index, no change to the interface info. + shill::NewInterfaceMessage message; + OnWiFiInterfaceInfoReceived(message); + VerifyInterfaceList(interface_list); + + // Message contain no interface type, no change to the interface info. + message.attributes()->CreateNl80211Attribute( + NL80211_ATTR_IFINDEX, shill::NetlinkMessage::MessageContext()); + message.attributes()->SetU32AttributeValue(NL80211_ATTR_IFINDEX, + kTestInterface0Index); + OnWiFiInterfaceInfoReceived(message); + + // Message contain interface type, interface info should be updated with + // the interface type, and a new Nl80211 message should be send to query for + // the PHY info. + EXPECT_CALL(netlink_manager_, SendNl80211Message( + IsGetInfoMessage(NL80211_CMD_GET_WIPHY, kTestInterface0Index), + _, _, _)).Times(1); + message.attributes()->CreateNl80211Attribute( + NL80211_ATTR_IFTYPE, shill::NetlinkMessage::MessageContext()); + message.attributes()->SetU32AttributeValue(NL80211_ATTR_IFTYPE, + NL80211_IFTYPE_AP); + OnWiFiInterfaceInfoReceived(message); + interface_list[0].iface_type = NL80211_IFTYPE_AP; + VerifyInterfaceList(interface_list); +} + +TEST_F(DeviceInfoTest, ParsePhyInfoForWifiInterface) { + // Register a mock device. + scoped_refptr<MockDevice> device = new MockDevice(); + device->SetDeviceName(kTestDeviceName); + EXPECT_CALL(manager_, RegisterDevice(_)).Times(1); + RegisterDevice(device); + + // PHY info message. + shill::NewWiphyMessage message; + message.attributes()->CreateNl80211Attribute( + NL80211_ATTR_WIPHY_NAME, shill::NetlinkMessage::MessageContext()); + message.attributes()->SetStringAttributeValue(NL80211_ATTR_WIPHY_NAME, + kTestDeviceName); + + // Receive PHY info message for an interface that have not been detected yet. + EXPECT_CALL(*device.get(), RegisterInterface(_)).Times(0); + OnWiFiInterfacePhyInfoReceived(kTestInterface0Index, message); + + // Pretend interface is detected through AddLink with interface info already + // received (interface type), and still missing PHY info for that interface. + Device::WiFiInterface interface( + kTestInterface0Name, "", kTestInterface0Index, NL80211_IFTYPE_AP); + AddInterface(interface); + + // PHY info is received for a detected interface, should register that + // interface to the corresponding Device. + interface.device_name = kTestDeviceName; + EXPECT_CALL(*device.get(), + RegisterInterface(IsInterface(interface))).Times(1); + OnWiFiInterfacePhyInfoReceived(kTestInterface0Index, message); +} + +TEST_F(DeviceInfoTest, ReceivePhyInfoBeforePhyIsEnumerated) { + // New interface is detected. + Device::WiFiInterface interface( + kTestInterface0Name, "", kTestInterface0Index, NL80211_IFTYPE_AP); + AddInterface(interface); + vector<Device::WiFiInterface> interface_list; + interface_list.push_back(interface); + + // Received PHY info for the interface when the corresponding PHY is not + // enumerated yet, new device should be created and register to manager. + shill::NewWiphyMessage message; + message.attributes()->CreateNl80211Attribute( + NL80211_ATTR_WIPHY_NAME, shill::NetlinkMessage::MessageContext()); + message.attributes()->SetStringAttributeValue(NL80211_ATTR_WIPHY_NAME, + kTestDeviceName); + EXPECT_CALL(manager_, RegisterDevice(IsDevice(kTestDeviceName))).Times(1); + OnWiFiInterfacePhyInfoReceived(kTestInterface0Index, message); + interface_list[0].device_name = kTestDeviceName; + VerifyInterfaceList(interface_list); +} + +TEST_F(DeviceInfoTest, RegisterDevice) { + vector<scoped_refptr<Device>> device_list; + + // Register a nullptr. + RegisterDevice(nullptr); + VerifyDeviceList(device_list); + + // Register a device. + device_list.push_back(new Device(&manager_, kTestDeviceName)); + EXPECT_CALL(manager_, RegisterDevice(device_list[0])); + RegisterDevice(device_list[0]); + VerifyDeviceList(device_list); +} + +} // namespace apmanager |