blob: e3dfd4711d6147e20967402983dbe0b4a6ac4e4c [file] [log] [blame]
// Copyright (c) 2011 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 <glib.h>
#include <sys/socket.h>
#include <linux/netlink.h> // Needs typedefs from sys/socket.h.
#include <linux/rtnetlink.h>
#include <base/callback_old.h>
#include <base/logging.h>
#include <base/memory/ref_counted.h>
#include <base/message_loop.h>
#include <base/stl_util-inl.h>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "shill/device_info.h"
#include "shill/dhcp_provider.h"
#include "shill/manager.h"
#include "shill/mock_control.h"
#include "shill/mock_glib.h"
#include "shill/mock_sockets.h"
#include "shill/rtnl_handler.h"
using std::string;
using testing::_;
using testing::Return;
using testing::Test;
namespace shill {
class TestEventDispatcher : public EventDispatcher {
public:
virtual IOInputHandler *CreateInputHandler(
int fd,
Callback1<InputData*>::Type *callback) {
return NULL;
}
};
class DeviceInfoTest : public Test {
public:
DeviceInfoTest()
: manager_(&control_interface_, &dispatcher_, &glib_),
device_info_(&control_interface_, &dispatcher_, &manager_),
task_factory_(this) {
DHCPProvider::GetInstance()->glib_ = &glib_;
}
base::hash_map<int, DeviceRefPtr> *DeviceInfoDevices() {
return &device_info_.devices_;
}
void AddLink();
virtual void TearDown() {
RTNLHandler::GetInstance()->Stop();
}
protected:
static const int kTestSocket;
static const int kTestDeviceIndex;
static const char kTestDeviceName[];
void StartRTNLHandler();
void StopRTNLHandler();
MockGLib glib_;
MockSockets sockets_;
MockControl control_interface_;
Manager manager_;
DeviceInfo device_info_;
TestEventDispatcher dispatcher_;
ScopedRunnableMethodFactory<DeviceInfoTest> task_factory_;
};
const int DeviceInfoTest::kTestSocket = 123;
const int DeviceInfoTest::kTestDeviceIndex = 123456;
const char DeviceInfoTest::kTestDeviceName[] = "test-device";
void DeviceInfoTest::StartRTNLHandler() {
EXPECT_CALL(sockets_, Socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE))
.WillOnce(Return(kTestSocket));
EXPECT_CALL(sockets_, Bind(kTestSocket, _, sizeof(sockaddr_nl)))
.WillOnce(Return(0));
RTNLHandler::GetInstance()->Start(&dispatcher_, &sockets_);
}
void DeviceInfoTest::StopRTNLHandler() {
EXPECT_CALL(sockets_, Close(kTestSocket)).WillOnce(Return(0));
RTNLHandler::GetInstance()->Stop();
}
void DeviceInfoTest::AddLink() {
// TODO(petkov): Abstract this away into a separate message parsing/creation
// class.
struct {
struct nlmsghdr hdr;
struct ifinfomsg msg;
char attrs[256];
} message;
memset(&message, 0, sizeof(message));
message.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(message.msg));
message.hdr.nlmsg_type = RTM_NEWLINK;
message.msg.ifi_index = kTestDeviceIndex;
// Append the interface name attribute.
string link_name = kTestDeviceName;
int len = RTA_LENGTH(link_name.size() + 1);
size_t new_msg_size = NLMSG_ALIGN(message.hdr.nlmsg_len) + RTA_ALIGN(len);
ASSERT_TRUE(new_msg_size <= sizeof(message));
struct rtattr *rt_attr =
reinterpret_cast<struct rtattr *>(reinterpret_cast<unsigned char *>
(&message) +
NLMSG_ALIGN(message.hdr.nlmsg_len));
rt_attr->rta_type = IFLA_IFNAME;
rt_attr->rta_len = len;
memcpy(RTA_DATA(rt_attr), link_name.c_str(), link_name.size() + 1);
message.hdr.nlmsg_len = new_msg_size;
InputData data(reinterpret_cast<unsigned char *>(&message),
message.hdr.nlmsg_len);
RTNLHandler::GetInstance()->ParseRTNL(&data);
}
TEST_F(DeviceInfoTest, DeviceEnumeration) {
// Start our own private device_info
device_info_.Start();
EXPECT_CALL(sockets_, SendTo(kTestSocket, _, _, 0, _, sizeof(sockaddr_nl)))
.WillOnce(Return(0));
StartRTNLHandler();
AddLink();
// Peek in at the map of devices
base::hash_map<int, DeviceRefPtr> *device_map = DeviceInfoDevices();
EXPECT_EQ(1, device_map->size());
EXPECT_TRUE(ContainsKey(*device_map, kTestDeviceIndex));
device_info_.Stop();
StopRTNLHandler();
}
TEST_F(DeviceInfoTest, DeviceEnumerationReverse) {
StartRTNLHandler();
EXPECT_CALL(sockets_, SendTo(kTestSocket, _, _, 0, _, sizeof(sockaddr_nl)))
.WillOnce(Return(0));
device_info_.Start();
AddLink();
// Peek in at the map of devices
base::hash_map<int, DeviceRefPtr> *device_map = DeviceInfoDevices();
EXPECT_EQ(1, device_map->size());
EXPECT_TRUE(ContainsKey(*device_map, kTestDeviceIndex));
StopRTNLHandler();
device_info_.Stop();
}
TEST_F(DeviceInfoTest, DeviceBlackList) {
device_info_.AddDeviceToBlackList(kTestDeviceName);
device_info_.Start();
EXPECT_CALL(sockets_, SendTo(_, _, _, _, _, _));
StartRTNLHandler();
AddLink();
// Peek in at the map of devices
base::hash_map<int, DeviceRefPtr> *device_map(DeviceInfoDevices());
ASSERT_TRUE(ContainsKey(*device_map, kTestDeviceIndex));
EXPECT_TRUE(device_map->find(kTestDeviceIndex)->second->TechnologyIs(
Device::kBlacklisted));
StopRTNLHandler();
device_info_.Stop();
}
} // namespace shill