1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chromeos/dbus/gsm_sms_client.h"
6
7#include "base/bind.h"
8#include "base/message_loop/message_loop.h"
9#include "base/values.h"
10#include "dbus/message.h"
11#include "dbus/mock_bus.h"
12#include "dbus/mock_object_proxy.h"
13#include "dbus/object_path.h"
14#include "dbus/values_util.h"
15#include "testing/gmock/include/gmock/gmock.h"
16#include "testing/gtest/include/gtest/gtest.h"
17#include "third_party/cros_system_api/dbus/service_constants.h"
18
19using ::testing::_;
20using ::testing::Invoke;
21using ::testing::Return;
22
23namespace chromeos {
24
25namespace {
26
27// A mock SmsReceivedHandler.
28class MockSmsReceivedHandler {
29 public:
30  MOCK_METHOD2(Run, void(uint32 index, bool complete));
31};
32
33// A mock DeleteCallback.
34class MockDeleteCallback {
35 public:
36  MOCK_METHOD0(Run, void());
37};
38
39// A mock GetCallback.
40class MockGetCallback {
41 public:
42  MOCK_METHOD1(Run, void(const base::DictionaryValue& sms));
43};
44
45// A mock ListCallback.
46class MockListCallback {
47 public:
48  MOCK_METHOD1(Run, void(const base::ListValue& result));
49};
50
51// D-Bus service name used by test.
52const char kServiceName[] = "service.name";
53// D-Bus object path used by test.
54const char kObjectPath[] = "/object/path";
55
56// Keys of SMS dictionary.
57const char kNumberKey[] = "number";
58const char kTextKey[] = "text";
59
60// Example values of SMS dictionary.
61const char kExampleNumber[] = "00012345678";
62const char kExampleText[] = "Hello.";
63
64}  // namespace
65
66class GsmSMSClientTest : public testing::Test {
67 public:
68  GsmSMSClientTest() : expected_index_(0),
69                       response_(NULL),
70                       expected_result_(NULL) {}
71
72  virtual void SetUp() OVERRIDE {
73    // Create a mock bus.
74    dbus::Bus::Options options;
75    options.bus_type = dbus::Bus::SYSTEM;
76    mock_bus_ = new dbus::MockBus(options);
77
78    // Create a mock proxy.
79    mock_proxy_ = new dbus::MockObjectProxy(mock_bus_.get(),
80                                            kServiceName,
81                                            dbus::ObjectPath(kObjectPath));
82
83    // Set an expectation so mock_proxy's ConnectToSignal() will use
84    // OnConnectToSignal() to run the callback.
85    EXPECT_CALL(*mock_proxy_.get(),
86                ConnectToSignal(modemmanager::kModemManagerSMSInterface,
87                                modemmanager::kSMSReceivedSignal,
88                                _,
89                                _))
90        .WillRepeatedly(Invoke(this, &GsmSMSClientTest::OnConnectToSignal));
91
92    // Set an expectation so mock_bus's GetObjectProxy() for the given
93    // service name and the object path will return mock_proxy_.
94    EXPECT_CALL(*mock_bus_.get(),
95                GetObjectProxy(kServiceName, dbus::ObjectPath(kObjectPath)))
96        .WillOnce(Return(mock_proxy_.get()));
97
98    // ShutdownAndBlock() will be called in TearDown().
99    EXPECT_CALL(*mock_bus_.get(), ShutdownAndBlock()).WillOnce(Return());
100
101    // Create a client with the mock bus.
102    client_.reset(GsmSMSClient::Create());
103    client_->Init(mock_bus_.get());
104  }
105
106  virtual void TearDown() OVERRIDE {
107    mock_bus_->ShutdownAndBlock();
108  }
109
110  // Handles Delete method call.
111  void OnDelete(dbus::MethodCall* method_call,
112                int timeout_ms,
113                const dbus::ObjectProxy::ResponseCallback& callback) {
114    EXPECT_EQ(modemmanager::kModemManagerSMSInterface,
115              method_call->GetInterface());
116    EXPECT_EQ(modemmanager::kSMSDeleteFunction, method_call->GetMember());
117    uint32 index = 0;
118    dbus::MessageReader reader(method_call);
119    EXPECT_TRUE(reader.PopUint32(&index));
120    EXPECT_EQ(expected_index_, index);
121    EXPECT_FALSE(reader.HasMoreData());
122
123    message_loop_.PostTask(FROM_HERE, base::Bind(callback, response_));
124  }
125
126  // Handles Get method call.
127  void OnGet(dbus::MethodCall* method_call,
128             int timeout_ms,
129             const dbus::ObjectProxy::ResponseCallback& callback) {
130    EXPECT_EQ(modemmanager::kModemManagerSMSInterface,
131              method_call->GetInterface());
132    EXPECT_EQ(modemmanager::kSMSGetFunction, method_call->GetMember());
133    uint32 index = 0;
134    dbus::MessageReader reader(method_call);
135    EXPECT_TRUE(reader.PopUint32(&index));
136    EXPECT_EQ(expected_index_, index);
137    EXPECT_FALSE(reader.HasMoreData());
138
139    message_loop_.PostTask(FROM_HERE, base::Bind(callback, response_));
140  }
141
142  // Handles List method call.
143  void OnList(dbus::MethodCall* method_call,
144              int timeout_ms,
145              const dbus::ObjectProxy::ResponseCallback& callback) {
146    EXPECT_EQ(modemmanager::kModemManagerSMSInterface,
147              method_call->GetInterface());
148    EXPECT_EQ(modemmanager::kSMSListFunction, method_call->GetMember());
149    dbus::MessageReader reader(method_call);
150    EXPECT_FALSE(reader.HasMoreData());
151
152    message_loop_.PostTask(FROM_HERE, base::Bind(callback, response_));
153  }
154
155  // Checks the results of Get and List.
156  void CheckResult(const base::Value& result) {
157    EXPECT_TRUE(result.Equals(expected_result_));
158  }
159
160 protected:
161  // The client to be tested.
162  scoped_ptr<GsmSMSClient> client_;
163  // A message loop to emulate asynchronous behavior.
164  base::MessageLoop message_loop_;
165  // The mock bus.
166  scoped_refptr<dbus::MockBus> mock_bus_;
167  // The mock object proxy.
168  scoped_refptr<dbus::MockObjectProxy> mock_proxy_;
169  // The SmsReceived signal handler given by the tested client.
170  dbus::ObjectProxy::SignalCallback sms_received_callback_;
171  // Expected argument for Delete and Get methods.
172  uint32 expected_index_;
173  // Response returned by mock methods.
174  dbus::Response* response_;
175  // Expected result of Get and List methods.
176  base::Value* expected_result_;
177
178 private:
179  // Used to implement the mock proxy.
180  void OnConnectToSignal(
181      const std::string& interface_name,
182      const std::string& signal_name,
183      const dbus::ObjectProxy::SignalCallback& signal_callback,
184      const dbus::ObjectProxy::OnConnectedCallback& on_connected_callback) {
185    sms_received_callback_ = signal_callback;
186    const bool success = true;
187    message_loop_.PostTask(FROM_HERE, base::Bind(on_connected_callback,
188                                                 interface_name,
189                                                 signal_name,
190                                                 success));
191  }
192};
193
194TEST_F(GsmSMSClientTest, SmsReceived) {
195  // Set expectations.
196  const uint32 kIndex = 42;
197  const bool kComplete = true;
198  MockSmsReceivedHandler handler;
199  EXPECT_CALL(handler, Run(kIndex, kComplete)).Times(1);
200  // Set handler.
201  client_->SetSmsReceivedHandler(kServiceName, dbus::ObjectPath(kObjectPath),
202                                 base::Bind(&MockSmsReceivedHandler::Run,
203                                            base::Unretained(&handler)));
204
205  // Run the message loop to run the signal connection result callback.
206  message_loop_.RunUntilIdle();
207
208  // Send signal.
209  dbus::Signal signal(modemmanager::kModemManagerSMSInterface,
210                      modemmanager::kSMSReceivedSignal);
211  dbus::MessageWriter writer(&signal);
212  writer.AppendUint32(kIndex);
213  writer.AppendBool(kComplete);
214  ASSERT_FALSE(sms_received_callback_.is_null());
215  sms_received_callback_.Run(&signal);
216  // Reset handler.
217  client_->ResetSmsReceivedHandler(kServiceName, dbus::ObjectPath(kObjectPath));
218  // Send signal again.
219  sms_received_callback_.Run(&signal);
220}
221
222TEST_F(GsmSMSClientTest, Delete) {
223  // Set expectations.
224  const uint32 kIndex = 42;
225  expected_index_ = kIndex;
226  EXPECT_CALL(*mock_proxy_.get(), CallMethod(_, _, _))
227      .WillOnce(Invoke(this, &GsmSMSClientTest::OnDelete));
228  MockDeleteCallback callback;
229  EXPECT_CALL(callback, Run()).Times(1);
230  // Create response.
231  scoped_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
232  response_ = response.get();
233  // Call Delete.
234  client_->Delete(kServiceName, dbus::ObjectPath(kObjectPath), kIndex,
235                  base::Bind(&MockDeleteCallback::Run,
236                             base::Unretained(&callback)));
237
238  // Run the message loop.
239  message_loop_.RunUntilIdle();
240}
241
242TEST_F(GsmSMSClientTest, Get) {
243  // Set expectations.
244  const uint32 kIndex = 42;
245  expected_index_ = kIndex;
246  EXPECT_CALL(*mock_proxy_.get(), CallMethod(_, _, _))
247      .WillOnce(Invoke(this, &GsmSMSClientTest::OnGet));
248  MockGetCallback callback;
249  EXPECT_CALL(callback, Run(_))
250      .WillOnce(Invoke(this, &GsmSMSClientTest::CheckResult));
251  // Create response.
252  scoped_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
253  dbus::MessageWriter writer(response.get());
254  dbus::MessageWriter array_writer(NULL);
255  writer.OpenArray("{sv}", &array_writer);
256  dbus::MessageWriter entry_writer(NULL);
257  array_writer.OpenDictEntry(&entry_writer);
258  entry_writer.AppendString(kNumberKey);
259  entry_writer.AppendVariantOfString(kExampleNumber);
260  array_writer.CloseContainer(&entry_writer);
261  array_writer.OpenDictEntry(&entry_writer);
262  entry_writer.AppendString(kTextKey);
263  entry_writer.AppendVariantOfString(kExampleText);
264  array_writer.CloseContainer(&entry_writer);
265  writer.CloseContainer(&array_writer);
266  response_ = response.get();
267  // Create expected result.
268  base::DictionaryValue expected_result;
269  expected_result.SetWithoutPathExpansion(
270      kNumberKey, new base::StringValue(kExampleNumber));
271  expected_result.SetWithoutPathExpansion(kTextKey,
272                                          new base::StringValue(kExampleText));
273  expected_result_ = &expected_result;
274  // Call Delete.
275  client_->Get(kServiceName, dbus::ObjectPath(kObjectPath), kIndex,
276               base::Bind(&MockGetCallback::Run, base::Unretained(&callback)));
277
278  // Run the message loop.
279  message_loop_.RunUntilIdle();
280}
281
282TEST_F(GsmSMSClientTest, List) {
283  // Set expectations.
284  EXPECT_CALL(*mock_proxy_.get(), CallMethod(_, _, _))
285      .WillOnce(Invoke(this, &GsmSMSClientTest::OnList));
286  MockListCallback callback;
287  EXPECT_CALL(callback, Run(_))
288      .WillOnce(Invoke(this, &GsmSMSClientTest::CheckResult));
289  // Create response.
290  scoped_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
291  dbus::MessageWriter writer(response.get());
292  dbus::MessageWriter array_writer(NULL);
293  writer.OpenArray("a{sv}", &array_writer);
294  dbus::MessageWriter sub_array_writer(NULL);
295  array_writer.OpenArray("{sv}", &sub_array_writer);
296  dbus::MessageWriter entry_writer(NULL);
297  sub_array_writer.OpenDictEntry(&entry_writer);
298  entry_writer.AppendString(kNumberKey);
299  entry_writer.AppendVariantOfString(kExampleNumber);
300  sub_array_writer.CloseContainer(&entry_writer);
301  sub_array_writer.OpenDictEntry(&entry_writer);
302  entry_writer.AppendString(kTextKey);
303  entry_writer.AppendVariantOfString(kExampleText);
304  sub_array_writer.CloseContainer(&entry_writer);
305  array_writer.CloseContainer(&sub_array_writer);
306  writer.CloseContainer(&array_writer);
307  response_ = response.get();
308  // Create expected result.
309  base::ListValue expected_result;
310  base::DictionaryValue* sms = new base::DictionaryValue;
311  sms->SetWithoutPathExpansion(kNumberKey,
312                               new base::StringValue(kExampleNumber));
313  sms->SetWithoutPathExpansion(kTextKey, new base::StringValue(kExampleText));
314  expected_result.Append(sms);
315  expected_result_ = &expected_result;
316  // Call List.
317  client_->List(kServiceName, dbus::ObjectPath(kObjectPath),
318                base::Bind(&MockListCallback::Run,
319                           base::Unretained(&callback)));
320
321  // Run the message loop.
322  message_loop_.RunUntilIdle();
323}
324
325}  // namespace chromeos
326