Peter Qiu | 326b6cf | 2015-09-02 11:11:42 -0700 | [diff] [blame] | 1 | // |
| 2 | // Copyright (C) 2014 The Android Open Source Project |
| 3 | // |
| 4 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | // you may not use this file except in compliance with the License. |
| 6 | // You may obtain a copy of the License at |
| 7 | // |
| 8 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | // |
| 10 | // Unless required by applicable law or agreed to in writing, software |
| 11 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | // See the License for the specific language governing permissions and |
| 14 | // limitations under the License. |
| 15 | // |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 16 | |
| 17 | #include "apmanager/device_info.h" |
| 18 | |
| 19 | #include <linux/netlink.h> |
Paul Stewart | 2393956 | 2015-07-28 15:12:40 -0700 | [diff] [blame] | 20 | #include <linux/rtnetlink.h> |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 21 | |
| 22 | #include <map> |
| 23 | #include <string> |
| 24 | #include <vector> |
| 25 | |
| 26 | #include <base/files/file_util.h> |
| 27 | #include <base/files/scoped_temp_dir.h> |
| 28 | #include <gmock/gmock.h> |
| 29 | #include <gtest/gtest.h> |
| 30 | #include <shill/net/byte_string.h> |
| 31 | #include <shill/net/mock_netlink_manager.h> |
| 32 | #include "shill/net/netlink_message_matchers.h" |
| 33 | #include "shill/net/nl80211_attribute.h" |
| 34 | #include "shill/net/nl80211_message.h" |
| 35 | #include <shill/net/rtnl_message.h> |
| 36 | |
Peter Qiu | f933540 | 2015-11-16 12:09:16 -0800 | [diff] [blame] | 37 | #include "apmanager/fake_device_adaptor.h" |
| 38 | #include "apmanager/mock_control.h" |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 39 | #include "apmanager/mock_device.h" |
| 40 | #include "apmanager/mock_manager.h" |
| 41 | |
| 42 | using shill::ByteString; |
| 43 | using shill::Nl80211Message; |
| 44 | using shill::RTNLMessage; |
| 45 | using std::map; |
| 46 | using std::string; |
| 47 | using std::vector; |
| 48 | using ::testing::_; |
| 49 | using ::testing::Mock; |
Peter Qiu | f933540 | 2015-11-16 12:09:16 -0800 | [diff] [blame] | 50 | using ::testing::ReturnNew; |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 51 | |
| 52 | namespace apmanager { |
| 53 | |
| 54 | namespace { |
| 55 | |
| 56 | const char kTestDeviceName[] = "test-phy"; |
| 57 | const char kTestInterface0Name[] = "test-interface0"; |
| 58 | const char kTestInterface1Name[] = "test-interface1"; |
| 59 | const uint32_t kTestInterface0Index = 1000; |
| 60 | const uint32_t kTestInterface1Index = 1001; |
| 61 | |
| 62 | } // namespace |
| 63 | |
| 64 | class DeviceInfoTest : public testing::Test { |
| 65 | public: |
Peter Qiu | f933540 | 2015-11-16 12:09:16 -0800 | [diff] [blame] | 66 | DeviceInfoTest() |
| 67 | : manager_(&control_interface_), |
| 68 | device_info_(&manager_) {} |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 69 | virtual ~DeviceInfoTest() {} |
| 70 | |
| 71 | virtual void SetUp() { |
| 72 | // Setup temporary directory for device info files. |
| 73 | CHECK(temp_dir_.CreateUniqueTempDir()); |
| 74 | device_info_root_ = temp_dir_.path().Append("sys/class/net"); |
| 75 | device_info_.device_info_root_ = device_info_root_; |
| 76 | |
| 77 | // Setup mock pointers; |
| 78 | device_info_.netlink_manager_ = &netlink_manager_; |
Peter Qiu | f933540 | 2015-11-16 12:09:16 -0800 | [diff] [blame] | 79 | |
| 80 | ON_CALL(control_interface_, CreateDeviceAdaptorRaw()) |
| 81 | .WillByDefault(ReturnNew<FakeDeviceAdaptor>()); |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 82 | } |
| 83 | |
| 84 | bool IsWifiInterface(const string& interface_name) { |
| 85 | return device_info_.IsWifiInterface(interface_name); |
| 86 | } |
| 87 | |
| 88 | void CreateDeviceInfoFile(const string& interface_name, |
| 89 | const string& file_name, |
| 90 | const string& contents) { |
| 91 | base::FilePath info_path = |
| 92 | device_info_root_.Append(interface_name).Append(file_name); |
| 93 | EXPECT_TRUE(base::CreateDirectory(info_path.DirName())); |
| 94 | EXPECT_TRUE(base::WriteFile(info_path, contents.c_str(), contents.size())); |
| 95 | } |
| 96 | |
| 97 | void SendLinkMsg(RTNLMessage::Mode mode, |
| 98 | uint32_t interface_index, |
| 99 | const string& interface_name) { |
| 100 | RTNLMessage message(RTNLMessage::kTypeLink, |
| 101 | mode, |
| 102 | 0, |
| 103 | 0, |
| 104 | 0, |
| 105 | interface_index, |
| 106 | shill::IPAddress::kFamilyIPv4); |
| 107 | message.SetAttribute(static_cast<uint16_t>(IFLA_IFNAME), |
| 108 | ByteString(interface_name, true)); |
| 109 | device_info_.LinkMsgHandler(message); |
| 110 | } |
| 111 | |
| 112 | void VerifyInterfaceList(const vector<Device::WiFiInterface>& interfaces) { |
| 113 | // Verify number of elements in the interface infos map and interface index |
| 114 | // of the elements in the map. |
| 115 | EXPECT_EQ(interfaces.size(), device_info_.interface_infos_.size()); |
| 116 | for (const auto& interface : interfaces) { |
| 117 | map<uint32_t, Device::WiFiInterface>::iterator it = |
| 118 | device_info_.interface_infos_.find(interface.iface_index); |
| 119 | EXPECT_NE(device_info_.interface_infos_.end(), it); |
| 120 | EXPECT_TRUE(interface.Equals(it->second)); |
| 121 | } |
| 122 | } |
| 123 | |
| 124 | void VerifyDeviceList(const vector<scoped_refptr<Device>>& devices) { |
| 125 | // Verify number of elements in the device map and the elements in the map. |
| 126 | EXPECT_EQ(devices.size(), device_info_.devices_.size()); |
| 127 | for (const auto& device : devices) { |
| 128 | map<string, scoped_refptr<Device>>::iterator it = |
| 129 | device_info_.devices_.find(device->GetDeviceName()); |
| 130 | EXPECT_NE(device_info_.devices_.end(), it); |
| 131 | EXPECT_EQ(device, it->second); |
| 132 | } |
| 133 | } |
| 134 | void AddInterface(const Device::WiFiInterface& interface) { |
| 135 | device_info_.interface_infos_[interface.iface_index] = interface; |
| 136 | } |
| 137 | |
Peter Qiu | 1ff67a7 | 2014-11-22 07:06:10 -0800 | [diff] [blame] | 138 | void OnWiFiPhyInfoReceived(const Nl80211Message& message) { |
| 139 | device_info_.OnWiFiPhyInfoReceived(message); |
| 140 | } |
| 141 | |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 142 | void OnWiFiInterfaceInfoReceived(const Nl80211Message& message) { |
| 143 | device_info_.OnWiFiInterfaceInfoReceived(message); |
| 144 | } |
| 145 | |
| 146 | void OnWiFiInterfacePhyInfoReceived(uint32_t interface_index, |
| 147 | const Nl80211Message& message) { |
| 148 | device_info_.OnWiFiInterfacePhyInfoReceived(interface_index, message); |
| 149 | } |
| 150 | |
| 151 | void RegisterDevice(scoped_refptr<Device> device) { |
| 152 | device_info_.RegisterDevice(device); |
| 153 | } |
| 154 | |
| 155 | protected: |
Peter Qiu | f933540 | 2015-11-16 12:09:16 -0800 | [diff] [blame] | 156 | MockControl control_interface_; |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 157 | MockManager manager_; |
Peter Qiu | f933540 | 2015-11-16 12:09:16 -0800 | [diff] [blame] | 158 | |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 159 | shill::MockNetlinkManager netlink_manager_; |
| 160 | base::ScopedTempDir temp_dir_; |
| 161 | base::FilePath device_info_root_; |
Peter Qiu | f933540 | 2015-11-16 12:09:16 -0800 | [diff] [blame] | 162 | DeviceInfo device_info_; |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 163 | }; |
| 164 | |
| 165 | MATCHER_P2(IsGetInfoMessage, command, index, "") { |
| 166 | if (arg->message_type() != Nl80211Message::GetMessageType()) { |
| 167 | return false; |
| 168 | } |
| 169 | const Nl80211Message *msg = reinterpret_cast<const Nl80211Message *>(arg); |
| 170 | if (msg->command() != command) { |
| 171 | return false; |
| 172 | } |
| 173 | uint32_t interface_index; |
| 174 | if (!msg->const_attributes()->GetU32AttributeValue(NL80211_ATTR_IFINDEX, |
| 175 | &interface_index)) { |
| 176 | return false; |
| 177 | } |
| 178 | // kInterfaceIndex is signed, but the attribute as handed from the kernel |
| 179 | // is unsigned. We're silently casting it away with this assignment. |
| 180 | uint32_t test_interface_index = index; |
| 181 | return interface_index == test_interface_index; |
| 182 | } |
| 183 | |
| 184 | MATCHER_P(IsInterface, interface, "") { |
| 185 | return arg.Equals(interface); |
| 186 | } |
| 187 | |
Peter Qiu | 1ff67a7 | 2014-11-22 07:06:10 -0800 | [diff] [blame] | 188 | MATCHER_P(IsDevice, device_name, "") { |
| 189 | return arg->GetDeviceName() == device_name; |
| 190 | } |
| 191 | |
| 192 | TEST_F(DeviceInfoTest, EnumerateDevices) { |
| 193 | shill::NewWiphyMessage message; |
| 194 | |
| 195 | // No device name in the message, failed to create device. |
| 196 | EXPECT_CALL(manager_, RegisterDevice(_)).Times(0); |
| 197 | OnWiFiPhyInfoReceived(message); |
| 198 | |
| 199 | // Device name in the message, device should be created/register to manager. |
Samuel Tan | 3109666 | 2015-02-04 14:44:10 -0800 | [diff] [blame] | 200 | message.attributes()->CreateNl80211Attribute( |
| 201 | NL80211_ATTR_WIPHY_NAME, shill::NetlinkMessage::MessageContext()); |
Peter Qiu | 1ff67a7 | 2014-11-22 07:06:10 -0800 | [diff] [blame] | 202 | message.attributes()->SetStringAttributeValue(NL80211_ATTR_WIPHY_NAME, |
| 203 | kTestDeviceName); |
| 204 | EXPECT_CALL(manager_, RegisterDevice(IsDevice(kTestDeviceName))).Times(1); |
| 205 | OnWiFiPhyInfoReceived(message); |
| 206 | Mock::VerifyAndClearExpectations(&manager_); |
| 207 | |
| 208 | // Receive a message for a device already created, should not create/register |
| 209 | // device again. |
| 210 | EXPECT_CALL(manager_, RegisterDevice(_)).Times(0); |
| 211 | OnWiFiPhyInfoReceived(message); |
| 212 | } |
| 213 | |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 214 | TEST_F(DeviceInfoTest, IsWiFiInterface) { |
| 215 | // No device info file exist, not a wifi interface. |
| 216 | EXPECT_FALSE(IsWifiInterface(kTestInterface0Name)); |
| 217 | |
| 218 | // Device info for an ethernet device, not a wifi interface |
| 219 | CreateDeviceInfoFile(kTestInterface0Name, "uevent", "INTERFACE=eth0\n"); |
| 220 | EXPECT_FALSE(IsWifiInterface(kTestInterface0Name)); |
| 221 | |
| 222 | // Device info for a wifi interface. |
| 223 | CreateDeviceInfoFile(kTestInterface1Name, "uevent", "DEVTYPE=wlan\n"); |
| 224 | EXPECT_TRUE(IsWifiInterface(kTestInterface1Name)); |
| 225 | } |
| 226 | |
| 227 | TEST_F(DeviceInfoTest, InterfaceDetection) { |
| 228 | vector<Device::WiFiInterface> interface_list; |
| 229 | // Ignore non-wifi interface. |
| 230 | SendLinkMsg(RTNLMessage::kModeAdd, |
| 231 | kTestInterface0Index, |
| 232 | kTestInterface0Name); |
| 233 | VerifyInterfaceList(interface_list); |
| 234 | |
| 235 | // AddLink event for wifi interface. |
| 236 | CreateDeviceInfoFile(kTestInterface0Name, "uevent", "DEVTYPE=wlan\n"); |
| 237 | EXPECT_CALL(netlink_manager_, SendNl80211Message( |
| 238 | IsGetInfoMessage(NL80211_CMD_GET_INTERFACE, kTestInterface0Index), |
| 239 | _, _, _)).Times(1); |
| 240 | SendLinkMsg(RTNLMessage::kModeAdd, |
| 241 | kTestInterface0Index, |
| 242 | kTestInterface0Name); |
| 243 | interface_list.push_back(Device::WiFiInterface( |
| 244 | kTestInterface0Name, "", kTestInterface0Index, 0)); |
| 245 | VerifyInterfaceList(interface_list); |
| 246 | Mock::VerifyAndClearExpectations(&netlink_manager_); |
| 247 | |
| 248 | // AddLink event for another wifi interface. |
| 249 | CreateDeviceInfoFile(kTestInterface1Name, "uevent", "DEVTYPE=wlan\n"); |
| 250 | EXPECT_CALL(netlink_manager_, SendNl80211Message( |
| 251 | IsGetInfoMessage(NL80211_CMD_GET_INTERFACE, kTestInterface1Index), |
| 252 | _, _, _)).Times(1); |
| 253 | SendLinkMsg(RTNLMessage::kModeAdd, |
| 254 | kTestInterface1Index, |
| 255 | kTestInterface1Name); |
| 256 | interface_list.push_back(Device::WiFiInterface( |
| 257 | kTestInterface1Name, "", kTestInterface1Index, 0)); |
| 258 | VerifyInterfaceList(interface_list); |
| 259 | Mock::VerifyAndClearExpectations(&netlink_manager_); |
| 260 | |
| 261 | // AddLink event for an interface that's already added, no change to interface |
| 262 | // list. |
| 263 | EXPECT_CALL(netlink_manager_, SendNl80211Message(_, _, _, _)).Times(0); |
| 264 | SendLinkMsg(RTNLMessage::kModeAdd, |
| 265 | kTestInterface0Index, |
| 266 | kTestInterface0Name); |
| 267 | VerifyInterfaceList(interface_list); |
| 268 | Mock::VerifyAndClearExpectations(&netlink_manager_); |
| 269 | |
| 270 | // Remove the first wifi interface. |
| 271 | SendLinkMsg(RTNLMessage::kModeDelete, |
| 272 | kTestInterface0Index, |
| 273 | kTestInterface0Name); |
| 274 | interface_list.clear(); |
| 275 | interface_list.push_back(Device::WiFiInterface( |
| 276 | kTestInterface1Name, "", kTestInterface1Index, 0)); |
| 277 | VerifyInterfaceList(interface_list); |
| 278 | |
| 279 | // Remove the non-exist interface, no change to the list. |
| 280 | SendLinkMsg(RTNLMessage::kModeDelete, |
| 281 | kTestInterface0Index, |
| 282 | kTestInterface0Name); |
| 283 | VerifyInterfaceList(interface_list); |
| 284 | |
| 285 | // Remove the last interface, list should be empty now. |
| 286 | SendLinkMsg(RTNLMessage::kModeDelete, |
| 287 | kTestInterface1Index, |
| 288 | kTestInterface1Name); |
| 289 | interface_list.clear(); |
| 290 | VerifyInterfaceList(interface_list); |
| 291 | } |
| 292 | |
| 293 | TEST_F(DeviceInfoTest, ParseWifiInterfaceInfo) { |
| 294 | // Add an interface without interface type info. |
| 295 | Device::WiFiInterface interface( |
| 296 | kTestInterface0Name, "", kTestInterface0Index, 0); |
| 297 | AddInterface(interface); |
| 298 | vector<Device::WiFiInterface> interface_list; |
| 299 | interface_list.push_back(interface); |
| 300 | |
| 301 | // Message contain no interface index, no change to the interface info. |
| 302 | shill::NewInterfaceMessage message; |
| 303 | OnWiFiInterfaceInfoReceived(message); |
| 304 | VerifyInterfaceList(interface_list); |
| 305 | |
| 306 | // Message contain no interface type, no change to the interface info. |
Samuel Tan | 3109666 | 2015-02-04 14:44:10 -0800 | [diff] [blame] | 307 | message.attributes()->CreateNl80211Attribute( |
| 308 | NL80211_ATTR_IFINDEX, shill::NetlinkMessage::MessageContext()); |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 309 | message.attributes()->SetU32AttributeValue(NL80211_ATTR_IFINDEX, |
| 310 | kTestInterface0Index); |
| 311 | OnWiFiInterfaceInfoReceived(message); |
| 312 | |
| 313 | // Message contain interface type, interface info should be updated with |
| 314 | // the interface type, and a new Nl80211 message should be send to query for |
| 315 | // the PHY info. |
| 316 | EXPECT_CALL(netlink_manager_, SendNl80211Message( |
| 317 | IsGetInfoMessage(NL80211_CMD_GET_WIPHY, kTestInterface0Index), |
| 318 | _, _, _)).Times(1); |
Samuel Tan | 3109666 | 2015-02-04 14:44:10 -0800 | [diff] [blame] | 319 | message.attributes()->CreateNl80211Attribute( |
| 320 | NL80211_ATTR_IFTYPE, shill::NetlinkMessage::MessageContext()); |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 321 | message.attributes()->SetU32AttributeValue(NL80211_ATTR_IFTYPE, |
| 322 | NL80211_IFTYPE_AP); |
| 323 | OnWiFiInterfaceInfoReceived(message); |
| 324 | interface_list[0].iface_type = NL80211_IFTYPE_AP; |
| 325 | VerifyInterfaceList(interface_list); |
| 326 | } |
| 327 | |
| 328 | TEST_F(DeviceInfoTest, ParsePhyInfoForWifiInterface) { |
| 329 | // Register a mock device. |
Peter Qiu | f933540 | 2015-11-16 12:09:16 -0800 | [diff] [blame] | 330 | scoped_refptr<MockDevice> device = new MockDevice(&manager_); |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 331 | device->SetDeviceName(kTestDeviceName); |
| 332 | EXPECT_CALL(manager_, RegisterDevice(_)).Times(1); |
| 333 | RegisterDevice(device); |
| 334 | |
| 335 | // PHY info message. |
| 336 | shill::NewWiphyMessage message; |
Samuel Tan | 3109666 | 2015-02-04 14:44:10 -0800 | [diff] [blame] | 337 | message.attributes()->CreateNl80211Attribute( |
| 338 | NL80211_ATTR_WIPHY_NAME, shill::NetlinkMessage::MessageContext()); |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 339 | message.attributes()->SetStringAttributeValue(NL80211_ATTR_WIPHY_NAME, |
| 340 | kTestDeviceName); |
| 341 | |
| 342 | // Receive PHY info message for an interface that have not been detected yet. |
| 343 | EXPECT_CALL(*device.get(), RegisterInterface(_)).Times(0); |
| 344 | OnWiFiInterfacePhyInfoReceived(kTestInterface0Index, message); |
| 345 | |
| 346 | // Pretend interface is detected through AddLink with interface info already |
| 347 | // received (interface type), and still missing PHY info for that interface. |
| 348 | Device::WiFiInterface interface( |
| 349 | kTestInterface0Name, "", kTestInterface0Index, NL80211_IFTYPE_AP); |
| 350 | AddInterface(interface); |
| 351 | |
| 352 | // PHY info is received for a detected interface, should register that |
| 353 | // interface to the corresponding Device. |
| 354 | interface.device_name = kTestDeviceName; |
| 355 | EXPECT_CALL(*device.get(), |
| 356 | RegisterInterface(IsInterface(interface))).Times(1); |
| 357 | OnWiFiInterfacePhyInfoReceived(kTestInterface0Index, message); |
| 358 | } |
| 359 | |
| 360 | TEST_F(DeviceInfoTest, ReceivePhyInfoBeforePhyIsEnumerated) { |
| 361 | // New interface is detected. |
| 362 | Device::WiFiInterface interface( |
| 363 | kTestInterface0Name, "", kTestInterface0Index, NL80211_IFTYPE_AP); |
| 364 | AddInterface(interface); |
Peter Qiu | 1ff67a7 | 2014-11-22 07:06:10 -0800 | [diff] [blame] | 365 | vector<Device::WiFiInterface> interface_list; |
| 366 | interface_list.push_back(interface); |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 367 | |
| 368 | // Received PHY info for the interface when the corresponding PHY is not |
Peter Qiu | 1ff67a7 | 2014-11-22 07:06:10 -0800 | [diff] [blame] | 369 | // enumerated yet, new device should be created and register to manager. |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 370 | shill::NewWiphyMessage message; |
Samuel Tan | 3109666 | 2015-02-04 14:44:10 -0800 | [diff] [blame] | 371 | message.attributes()->CreateNl80211Attribute( |
| 372 | NL80211_ATTR_WIPHY_NAME, shill::NetlinkMessage::MessageContext()); |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 373 | message.attributes()->SetStringAttributeValue(NL80211_ATTR_WIPHY_NAME, |
| 374 | kTestDeviceName); |
Peter Qiu | 1ff67a7 | 2014-11-22 07:06:10 -0800 | [diff] [blame] | 375 | EXPECT_CALL(manager_, RegisterDevice(IsDevice(kTestDeviceName))).Times(1); |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 376 | OnWiFiInterfacePhyInfoReceived(kTestInterface0Index, message); |
Peter Qiu | 1ff67a7 | 2014-11-22 07:06:10 -0800 | [diff] [blame] | 377 | interface_list[0].device_name = kTestDeviceName; |
| 378 | VerifyInterfaceList(interface_list); |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 379 | } |
| 380 | |
| 381 | TEST_F(DeviceInfoTest, RegisterDevice) { |
| 382 | vector<scoped_refptr<Device>> device_list; |
| 383 | |
| 384 | // Register a nullptr. |
| 385 | RegisterDevice(nullptr); |
| 386 | VerifyDeviceList(device_list); |
| 387 | |
| 388 | // Register a device. |
Peter Qiu | f933540 | 2015-11-16 12:09:16 -0800 | [diff] [blame] | 389 | device_list.push_back(new Device(&manager_, kTestDeviceName, 0)); |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 390 | EXPECT_CALL(manager_, RegisterDevice(device_list[0])); |
| 391 | RegisterDevice(device_list[0]); |
| 392 | VerifyDeviceList(device_list); |
| 393 | } |
| 394 | |
| 395 | } // namespace apmanager |