1// 2// Copyright (C) 2012 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// 16 17#include "shill/cellular/modem.h" 18 19#include <vector> 20 21#include <base/strings/stringprintf.h> 22#include <gmock/gmock.h> 23#include <gtest/gtest.h> 24#include <mm/mm-modem.h> 25#include <net/if.h> 26#include <sys/ioctl.h> 27 28#include "shill/cellular/cellular.h" 29#include "shill/cellular/cellular_capability_gsm.h" 30#include "shill/cellular/mock_cellular.h" 31#include "shill/cellular/mock_modem.h" 32#include "shill/cellular/mock_modem_info.h" 33#include "shill/manager.h" 34#include "shill/mock_device_info.h" 35#include "shill/net/mock_rtnl_handler.h" 36#include "shill/net/rtnl_handler.h" 37#include "shill/test_event_dispatcher.h" 38 39using std::string; 40using std::vector; 41using testing::_; 42using testing::AnyNumber; 43using testing::DoAll; 44using testing::Return; 45using testing::SetArgumentPointee; 46using testing::StrEq; 47using testing::StrictMock; 48using testing::Test; 49 50namespace shill { 51 52namespace { 53 54const int kTestInterfaceIndex = 5; 55const char kLinkName[] = "usb0"; 56const char kService[] = "org.chromium.ModemManager"; 57const char kPath[] = "/org/chromium/ModemManager/Gobi/0"; 58const unsigned char kAddress[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; 59const char kAddressAsString[] = "000102030405"; 60 61} // namespace 62 63class ModemTest : public Test { 64 public: 65 ModemTest() 66 : modem_info_(nullptr, &dispatcher_, nullptr, nullptr), 67 device_info_(modem_info_.control_interface(), modem_info_.dispatcher(), 68 modem_info_.metrics(), modem_info_.manager()), 69 modem_( 70 new StrictModem( 71 kService, 72 kPath, 73 &modem_info_, 74 nullptr)) {} 75 virtual void SetUp(); 76 virtual void TearDown(); 77 78 void ReplaceSingletons() { 79 modem_->rtnl_handler_ = &rtnl_handler_; 80 } 81 82 protected: 83 EventDispatcherForTest dispatcher_; 84 MockModemInfo modem_info_; 85 MockDeviceInfo device_info_; 86 std::unique_ptr<StrictModem> modem_; 87 MockRTNLHandler rtnl_handler_; 88 ByteString expected_address_; 89}; 90 91void ModemTest::SetUp() { 92 EXPECT_EQ(kService, modem_->service_); 93 EXPECT_EQ(kPath, modem_->path_); 94 ReplaceSingletons(); 95 expected_address_ = ByteString(kAddress, arraysize(kAddress)); 96 97 EXPECT_CALL(rtnl_handler_, GetInterfaceIndex(kLinkName)). 98 WillRepeatedly(Return(kTestInterfaceIndex)); 99 100 EXPECT_CALL(*modem_info_.mock_manager(), device_info()) 101 .WillRepeatedly(Return(&device_info_)); 102} 103 104void ModemTest::TearDown() { 105 modem_.reset(); 106} 107 108MATCHER_P2(HasPropertyWithValueU32, key, value, "") { 109 return arg.ContainsUint(key) && value == arg.GetUint(key); 110} 111 112TEST_F(ModemTest, PendingDevicePropertiesAndCreate) { 113 static const char kSentinel[] = "sentinel"; 114 static const uint32_t kSentinelValue = 17; 115 116 InterfaceToProperties properties; 117 properties[MM_MODEM_INTERFACE].SetUint(kSentinel, kSentinelValue); 118 119 EXPECT_CALL(*modem_, GetLinkName(_, _)).WillRepeatedly(DoAll( 120 SetArgumentPointee<1>(string(kLinkName)), 121 Return(true))); 122 EXPECT_CALL(rtnl_handler_, GetInterfaceIndex(StrEq(kLinkName))). 123 WillRepeatedly(Return(kTestInterfaceIndex)); 124 125 // The first time we call CreateDeviceFromModemProperties, 126 // GetMACAddress will fail. 127 EXPECT_CALL(device_info_, GetMACAddress(kTestInterfaceIndex, _)). 128 WillOnce(Return(false)); 129 EXPECT_CALL(*modem_, GetModemInterface()). 130 WillRepeatedly(Return(MM_MODEM_INTERFACE)); 131 modem_->CreateDeviceFromModemProperties(properties); 132 EXPECT_FALSE(modem_->device_.get()); 133 134 // On the second time, we allow GetMACAddress to succeed. Now we 135 // expect a device to be built 136 EXPECT_CALL(device_info_, GetMACAddress(kTestInterfaceIndex, _)). 137 WillOnce(DoAll(SetArgumentPointee<1>(expected_address_), 138 Return(true))); 139 140 // modem will take ownership 141 MockCellular* cellular = new MockCellular( 142 &modem_info_, 143 kLinkName, 144 kAddressAsString, 145 kTestInterfaceIndex, 146 Cellular::kTypeCDMA, 147 kService, 148 kPath); 149 150 EXPECT_CALL(*modem_, 151 ConstructCellular(StrEq(kLinkName), 152 StrEq(kAddressAsString), 153 kTestInterfaceIndex)). 154 WillOnce(Return(cellular)); 155 156 EXPECT_CALL(*cellular, OnPropertiesChanged( 157 _, 158 HasPropertyWithValueU32(kSentinel, kSentinelValue), 159 _)); 160 EXPECT_CALL(device_info_, RegisterDevice(_)); 161 modem_->OnDeviceInfoAvailable(kLinkName); 162 163 EXPECT_TRUE(modem_->device_.get()); 164 165 // Add expectations for the eventual |modem_| destruction. 166 EXPECT_CALL(*cellular, DestroyService()); 167 EXPECT_CALL(device_info_, DeregisterDevice(_)); 168} 169 170TEST_F(ModemTest, EarlyDeviceProperties) { 171 // OnDeviceInfoAvailable called before 172 // CreateDeviceFromModemProperties: Do nothing 173 modem_->OnDeviceInfoAvailable(kLinkName); 174 EXPECT_FALSE(modem_->device_.get()); 175} 176 177TEST_F(ModemTest, CreateDeviceEarlyFailures) { 178 InterfaceToProperties properties; 179 180 EXPECT_CALL(*modem_, ConstructCellular(_, _, _)).Times(0); 181 EXPECT_CALL(*modem_, GetModemInterface()). 182 WillRepeatedly(Return(MM_MODEM_INTERFACE)); 183 184 // No modem interface properties: no device created 185 modem_->CreateDeviceFromModemProperties(properties); 186 EXPECT_FALSE(modem_->device_.get()); 187 188 properties[MM_MODEM_INTERFACE] = KeyValueStore(); 189 190 // Link name, but no ifindex: no device created 191 EXPECT_CALL(*modem_, GetLinkName(_, _)).WillOnce(DoAll( 192 SetArgumentPointee<1>(string(kLinkName)), 193 Return(true))); 194 EXPECT_CALL(rtnl_handler_, GetInterfaceIndex(StrEq(kLinkName))).WillOnce( 195 Return(-1)); 196 modem_->CreateDeviceFromModemProperties(properties); 197 EXPECT_FALSE(modem_->device_.get()); 198 199 // The params are good, but the device is blacklisted. 200 EXPECT_CALL(*modem_, GetLinkName(_, _)).WillOnce(DoAll( 201 SetArgumentPointee<1>(string(kLinkName)), 202 Return(true))); 203 EXPECT_CALL(rtnl_handler_, GetInterfaceIndex(StrEq(kLinkName))) 204 .WillOnce(Return(kTestInterfaceIndex)); 205 EXPECT_CALL(device_info_, GetMACAddress(kTestInterfaceIndex, _)) 206 .WillOnce(DoAll(SetArgumentPointee<1>(expected_address_), 207 Return(true))); 208 EXPECT_CALL(device_info_, IsDeviceBlackListed(kLinkName)) 209 .WillRepeatedly(Return(true)); 210 modem_->CreateDeviceFromModemProperties(properties); 211 EXPECT_FALSE(modem_->device_.get()); 212 213 // No link name: see CreateDevicePPP. 214} 215 216TEST_F(ModemTest, CreateDevicePPP) { 217 InterfaceToProperties properties; 218 properties[MM_MODEM_INTERFACE] = KeyValueStore(); 219 220 string dev_name( 221 base::StringPrintf(Modem::kFakeDevNameFormat, Modem::fake_dev_serial_)); 222 223 // |modem_| will take ownership. 224 MockCellular* cellular = new MockCellular( 225 &modem_info_, 226 dev_name, 227 Modem::kFakeDevAddress, 228 Modem::kFakeDevInterfaceIndex, 229 Cellular::kTypeUniversal, 230 kService, 231 kPath); 232 233 EXPECT_CALL(*modem_, GetModemInterface()). 234 WillRepeatedly(Return(MM_MODEM_INTERFACE)); 235 // No link name: assumed to be a PPP dongle. 236 EXPECT_CALL(*modem_, GetLinkName(_, _)).WillOnce(Return(false)); 237 EXPECT_CALL(*modem_, 238 ConstructCellular(dev_name, 239 StrEq(Modem::kFakeDevAddress), 240 Modem::kFakeDevInterfaceIndex)). 241 WillOnce(Return(cellular)); 242 EXPECT_CALL(device_info_, RegisterDevice(_)); 243 244 modem_->CreateDeviceFromModemProperties(properties); 245 EXPECT_TRUE(modem_->device_.get()); 246 247 // Add expectations for the eventual |modem_| destruction. 248 EXPECT_CALL(*cellular, DestroyService()); 249 EXPECT_CALL(device_info_, DeregisterDevice(_)); 250} 251 252TEST_F(ModemTest, GetDeviceParams) { 253 string mac_address; 254 int interface_index = 2; 255 EXPECT_CALL(rtnl_handler_, GetInterfaceIndex(_)).WillOnce(Return(-1)); 256 EXPECT_CALL(device_info_, GetMACAddress(_, _)).Times(AnyNumber()) 257 .WillRepeatedly(Return(false)); 258 EXPECT_FALSE(modem_->GetDeviceParams(&mac_address, &interface_index)); 259 EXPECT_EQ(-1, interface_index); 260 261 EXPECT_CALL(rtnl_handler_, GetInterfaceIndex(_)).WillOnce(Return(-2)); 262 EXPECT_CALL(device_info_, GetMACAddress(_, _)).Times(AnyNumber()) 263 .WillRepeatedly(Return(false)); 264 EXPECT_FALSE(modem_->GetDeviceParams(&mac_address, &interface_index)); 265 EXPECT_EQ(-2, interface_index); 266 267 EXPECT_CALL(rtnl_handler_, GetInterfaceIndex(_)).WillOnce(Return(1)); 268 EXPECT_CALL(device_info_, GetMACAddress(_, _)).WillOnce(Return(false)); 269 EXPECT_FALSE(modem_->GetDeviceParams(&mac_address, &interface_index)); 270 EXPECT_EQ(1, interface_index); 271 272 EXPECT_CALL(rtnl_handler_, GetInterfaceIndex(_)).WillOnce(Return(2)); 273 EXPECT_CALL(device_info_, GetMACAddress(2, _)). 274 WillOnce(DoAll(SetArgumentPointee<1>(expected_address_), 275 Return(true))); 276 EXPECT_TRUE(modem_->GetDeviceParams(&mac_address, &interface_index)); 277 EXPECT_EQ(2, interface_index); 278 EXPECT_EQ(kAddressAsString, mac_address); 279} 280 281TEST_F(ModemTest, RejectPPPModem) { 282 // TODO(rochberg): Port this to ModemClassic 283} 284 285} // namespace shill 286