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
39 using std::string;
40 using std::vector;
41 using testing::_;
42 using testing::AnyNumber;
43 using testing::DoAll;
44 using testing::Return;
45 using testing::SetArgumentPointee;
46 using testing::StrEq;
47 using testing::StrictMock;
48 using testing::Test;
49
50 namespace shill {
51
52 namespace {
53
54 const int kTestInterfaceIndex = 5;
55 const char kLinkName[] = "usb0";
56 const char kService[] = "org.chromium.ModemManager";
57 const char kPath[] = "/org/chromium/ModemManager/Gobi/0";
58 const unsigned char kAddress[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
59 const char kAddressAsString[] = "000102030405";
60
61 } // namespace
62
63 class ModemTest : public Test {
64 public:
ModemTest()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
ReplaceSingletons()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
SetUp()91 void 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
TearDown()104 void ModemTest::TearDown() {
105 modem_.reset();
106 }
107
108 MATCHER_P2(HasPropertyWithValueU32, key, value, "") {
109 return arg.ContainsUint(key) && value == arg.GetUint(key);
110 }
111
TEST_F(ModemTest,PendingDevicePropertiesAndCreate)112 TEST_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
TEST_F(ModemTest,EarlyDeviceProperties)170 TEST_F(ModemTest, EarlyDeviceProperties) {
171 // OnDeviceInfoAvailable called before
172 // CreateDeviceFromModemProperties: Do nothing
173 modem_->OnDeviceInfoAvailable(kLinkName);
174 EXPECT_FALSE(modem_->device_.get());
175 }
176
TEST_F(ModemTest,CreateDeviceEarlyFailures)177 TEST_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
TEST_F(ModemTest,CreateDevicePPP)216 TEST_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
TEST_F(ModemTest,GetDeviceParams)252 TEST_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
TEST_F(ModemTest,RejectPPPModem)281 TEST_F(ModemTest, RejectPPPModem) {
282 // TODO(rochberg): Port this to ModemClassic
283 }
284
285 } // namespace shill
286