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