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_manager.h"
18
19#include <base/stl_util.h>
20#include <ModemManager/ModemManager.h>
21
22#include "shill/cellular/mock_dbus_objectmanager_proxy.h"
23#include "shill/cellular/mock_modem.h"
24#include "shill/cellular/mock_modem_info.h"
25#include "shill/cellular/mock_modem_manager_proxy.h"
26#include "shill/manager.h"
27#include "shill/mock_control.h"
28#include "shill/mock_manager.h"
29#include "shill/test_event_dispatcher.h"
30#include "shill/testing.h"
31
32using std::string;
33using std::shared_ptr;
34using std::vector;
35using testing::_;
36using testing::Invoke;
37using testing::Pointee;
38using testing::Return;
39using testing::SaveArg;
40using testing::StrEq;
41using testing::Test;
42
43namespace shill {
44
45class ModemManagerTest : public Test {
46 public:
47  ModemManagerTest()
48      : manager_(&control_, &dispatcher_, nullptr),
49        modem_info_(&control_, &dispatcher_, nullptr, &manager_) {}
50
51  virtual void SetUp() {
52    modem_.reset(
53        new StrictModem(kService, kModemPath, &modem_info_, &control_));
54  }
55
56 protected:
57  static const char kService[];
58  static const char kPath[];
59  static const char kModemPath[];
60
61  shared_ptr<StrictModem> modem_;
62
63  EventDispatcherForTest dispatcher_;
64  MockControl control_;
65  MockManager manager_;
66  MockModemInfo modem_info_;
67};
68
69const char ModemManagerTest::kService[] = "org.chromium.ModemManager";
70const char ModemManagerTest::kPath[] = "/org/chromium/ModemManager";
71const char ModemManagerTest::kModemPath[] = "/org/blah/Modem/blah/0";
72
73class ModemManagerForTest : public ModemManager {
74 public:
75  ModemManagerForTest(ControlInterface* control_interface,
76                      const string& service,
77                      const string& path,
78                      ModemInfo* modem_info)
79    : ModemManager(control_interface, service, path, modem_info) {}
80
81  MOCK_METHOD0(Start, void());
82  MOCK_METHOD0(Stop, void());
83};
84
85class ModemManagerCoreTest : public ModemManagerTest {
86 public:
87  ModemManagerCoreTest()
88      : ModemManagerTest(),
89        modem_manager_(&control_, kService, kPath, &modem_info_) {}
90
91 protected:
92  ModemManagerForTest modem_manager_;
93};
94
95TEST_F(ModemManagerCoreTest, ConnectDisconnect) {
96  EXPECT_FALSE(modem_manager_.service_connected_);
97  modem_manager_.Connect();
98  EXPECT_TRUE(modem_manager_.service_connected_);
99
100  modem_manager_.RecordAddedModem(modem_);
101  EXPECT_EQ(1, modem_manager_.modems_.size());
102
103  modem_manager_.ModemManager::Disconnect();
104  EXPECT_EQ(0, modem_manager_.modems_.size());
105  EXPECT_FALSE(modem_manager_.service_connected_);
106}
107
108TEST_F(ModemManagerCoreTest, AddRemoveModem) {
109  modem_manager_.Connect();
110  EXPECT_FALSE(modem_manager_.ModemExists(kModemPath));
111
112  // Remove non-existent modem path.
113  modem_manager_.RemoveModem(kModemPath);
114  EXPECT_FALSE(modem_manager_.ModemExists(kModemPath));
115
116  modem_manager_.RecordAddedModem(modem_);
117  EXPECT_TRUE(modem_manager_.ModemExists(kModemPath));
118
119  // Add an already added modem.
120  modem_manager_.RecordAddedModem(modem_);
121  EXPECT_TRUE(modem_manager_.ModemExists(kModemPath));
122
123  modem_manager_.RemoveModem(kModemPath);
124  EXPECT_FALSE(modem_manager_.ModemExists(kModemPath));
125
126  // Remove an already removed modem path.
127  modem_manager_.RemoveModem(kModemPath);
128  EXPECT_FALSE(modem_manager_.ModemExists(kModemPath));
129}
130
131class ModemManagerClassicMockInit : public ModemManagerClassic {
132 public:
133  ModemManagerClassicMockInit(ControlInterface* control_interface,
134                              const string& service,
135                              const string& path,
136                              ModemInfo* modem_info_) :
137      ModemManagerClassic(control_interface, service, path, modem_info_) {}
138
139  MOCK_METHOD1(InitModemClassic, void(shared_ptr<ModemClassic>));
140};
141
142class ModemManagerClassicTest : public ModemManagerTest {
143 public:
144  ModemManagerClassicTest()
145      : ModemManagerTest(),
146        modem_manager_(&control_, kService, kPath, &modem_info_),
147        proxy_(new MockModemManagerProxy()) {}
148
149 protected:
150  ModemManagerClassicMockInit modem_manager_;
151  MockModemManagerProxy* proxy_;
152};
153
154TEST_F(ModemManagerClassicTest, StartStop) {
155  EXPECT_EQ(nullptr, modem_manager_.proxy_.get());
156
157  EXPECT_CALL(control_, CreateModemManagerProxy(_, kPath, kService, _, _))
158      .WillOnce(Return(proxy_));
159  modem_manager_.Start();
160  EXPECT_NE(nullptr, modem_manager_.proxy_.get());
161
162  modem_manager_.Stop();
163  EXPECT_EQ(nullptr, modem_manager_.proxy_.get());
164}
165
166TEST_F(ModemManagerClassicTest, Connect) {
167  // Setup proxy.
168  modem_manager_.proxy_.reset(proxy_);
169
170  EXPECT_CALL(*proxy_, EnumerateDevices())
171      .WillOnce(Return(vector<string>(1, kModemPath)));
172  EXPECT_CALL(modem_manager_,
173              InitModemClassic(
174                  Pointee(Field(&Modem::path_, StrEq(kModemPath)))));
175  modem_manager_.Connect();
176  EXPECT_EQ(1, modem_manager_.modems_.size());
177  ASSERT_TRUE(ContainsKey(modem_manager_.modems_, kModemPath));
178}
179
180class ModemManager1MockInit : public ModemManager1 {
181 public:
182  ModemManager1MockInit(ControlInterface* control_interface,
183                        const string& service,
184                        const string& path,
185                        ModemInfo* modem_info_) :
186      ModemManager1(control_interface, service, path, modem_info_) {}
187  MOCK_METHOD2(InitModem1, void(shared_ptr<Modem1>,
188                                const InterfaceToProperties&));
189};
190
191
192class ModemManager1Test : public ModemManagerTest {
193 public:
194  ModemManager1Test()
195      : ModemManagerTest(),
196        modem_manager_(&control_, kService, kPath, &modem_info_),
197        proxy_(new MockDBusObjectManagerProxy()) {}
198
199 protected:
200  virtual void SetUp() {
201    proxy_->IgnoreSetCallbacks();
202  }
203
204  void Connect(const ObjectsWithProperties& expected_objects) {
205    ManagedObjectsCallback get_managed_objects_callback;
206    EXPECT_CALL(*proxy_, GetManagedObjects(_, _, _))
207        .WillOnce(SaveArg<1>(&get_managed_objects_callback));
208    modem_manager_.Connect();
209    get_managed_objects_callback.Run(expected_objects, Error());
210  }
211
212  static ObjectsWithProperties GetModemWithProperties() {
213    KeyValueStore o_fd_mm1_modem;
214
215    InterfaceToProperties properties;
216    properties[MM_DBUS_INTERFACE_MODEM] = o_fd_mm1_modem;
217
218    ObjectsWithProperties objects_with_properties;
219    objects_with_properties[kModemPath] = properties;
220
221    return objects_with_properties;
222  }
223
224  ModemManager1MockInit modem_manager_;
225  MockDBusObjectManagerProxy* proxy_;
226  MockControl control_;
227};
228
229TEST_F(ModemManager1Test, StartStop) {
230  EXPECT_EQ(nullptr, modem_manager_.proxy_.get());
231
232  EXPECT_CALL(control_, CreateDBusObjectManagerProxy(kPath, kService, _, _))
233      .WillOnce(Return(proxy_));
234  EXPECT_CALL(*proxy_, set_interfaces_added_callback(_));
235  EXPECT_CALL(*proxy_, set_interfaces_removed_callback(_));
236  modem_manager_.Start();
237  EXPECT_NE(nullptr, modem_manager_.proxy_.get());
238
239  modem_manager_.Stop();
240  EXPECT_EQ(nullptr, modem_manager_.proxy_.get());
241}
242
243TEST_F(ModemManager1Test, Connect) {
244  // Setup proxy.
245  modem_manager_.proxy_.reset(proxy_);
246
247  Connect(GetModemWithProperties());
248  EXPECT_EQ(1, modem_manager_.modems_.size());
249  EXPECT_TRUE(ContainsKey(modem_manager_.modems_, kModemPath));
250}
251
252TEST_F(ModemManager1Test, AddRemoveInterfaces) {
253  // Setup proxy.
254  modem_manager_.proxy_.reset(proxy_);
255
256  // Have nothing come back from GetManagedObjects
257  Connect(ObjectsWithProperties());
258  EXPECT_EQ(0, modem_manager_.modems_.size());
259
260  // Add an object that doesn't have a modem interface.  Nothing should be added
261  EXPECT_CALL(modem_manager_, InitModem1(_, _)).Times(0);
262  modem_manager_.OnInterfacesAddedSignal(kModemPath,
263                                         InterfaceToProperties());
264  EXPECT_EQ(0, modem_manager_.modems_.size());
265
266  // Actually add a modem
267  EXPECT_CALL(modem_manager_, InitModem1(_, _)).Times(1);
268  modem_manager_.OnInterfacesAddedSignal(kModemPath,
269                                         GetModemWithProperties()[kModemPath]);
270  EXPECT_EQ(1, modem_manager_.modems_.size());
271
272  // Remove an irrelevant interface
273  vector<string> not_including_modem_interface;
274  not_including_modem_interface.push_back("not.a.modem.interface");
275  modem_manager_.OnInterfacesRemovedSignal(kModemPath,
276                                           not_including_modem_interface);
277  EXPECT_EQ(1, modem_manager_.modems_.size());
278
279  // Remove the modem
280  vector<string> with_modem_interface;
281  with_modem_interface.push_back(MM_DBUS_INTERFACE_MODEM);
282  modem_manager_.OnInterfacesRemovedSignal(kModemPath, with_modem_interface);
283  EXPECT_EQ(0, modem_manager_.modems_.size());
284}
285
286}  // namespace shill
287