// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chromeos/dbus/gsm_sms_client.h" #include "base/bind.h" #include "base/location.h" #include "base/message_loop/message_loop.h" #include "base/single_thread_task_runner.h" #include "base/values.h" #include "dbus/message.h" #include "dbus/mock_bus.h" #include "dbus/mock_object_proxy.h" #include "dbus/object_path.h" #include "dbus/values_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/cros_system_api/dbus/service_constants.h" using ::testing::_; using ::testing::Invoke; using ::testing::Return; namespace chromeos { namespace { // A mock SmsReceivedHandler. class MockSmsReceivedHandler { public: MOCK_METHOD2(Run, void(uint32 index, bool complete)); }; // A mock DeleteCallback. class MockDeleteCallback { public: MOCK_METHOD0(Run, void()); }; // A mock GetCallback. class MockGetCallback { public: MOCK_METHOD1(Run, void(const base::DictionaryValue& sms)); }; // A mock ListCallback. class MockListCallback { public: MOCK_METHOD1(Run, void(const base::ListValue& result)); }; // D-Bus service name used by test. const char kServiceName[] = "service.name"; // D-Bus object path used by test. const char kObjectPath[] = "/object/path"; // Keys of SMS dictionary. const char kNumberKey[] = "number"; const char kTextKey[] = "text"; // Example values of SMS dictionary. const char kExampleNumber[] = "00012345678"; const char kExampleText[] = "Hello."; } // namespace class GsmSMSClientTest : public testing::Test { public: GsmSMSClientTest() : expected_index_(0), response_(NULL), expected_result_(NULL) {} void SetUp() override { // Create a mock bus. dbus::Bus::Options options; options.bus_type = dbus::Bus::SYSTEM; mock_bus_ = new dbus::MockBus(options); // Create a mock proxy. mock_proxy_ = new dbus::MockObjectProxy(mock_bus_.get(), kServiceName, dbus::ObjectPath(kObjectPath)); // Set an expectation so mock_proxy's ConnectToSignal() will use // OnConnectToSignal() to run the callback. EXPECT_CALL(*mock_proxy_.get(), ConnectToSignal(modemmanager::kModemManagerSMSInterface, modemmanager::kSMSReceivedSignal, _, _)) .WillRepeatedly(Invoke(this, &GsmSMSClientTest::OnConnectToSignal)); // Set an expectation so mock_bus's GetObjectProxy() for the given // service name and the object path will return mock_proxy_. EXPECT_CALL(*mock_bus_.get(), GetObjectProxy(kServiceName, dbus::ObjectPath(kObjectPath))) .WillOnce(Return(mock_proxy_.get())); // ShutdownAndBlock() will be called in TearDown(). EXPECT_CALL(*mock_bus_.get(), ShutdownAndBlock()).WillOnce(Return()); // Create a client with the mock bus. client_.reset(GsmSMSClient::Create()); client_->Init(mock_bus_.get()); } void TearDown() override { mock_bus_->ShutdownAndBlock(); } // Handles Delete method call. void OnDelete(dbus::MethodCall* method_call, int timeout_ms, const dbus::ObjectProxy::ResponseCallback& callback) { EXPECT_EQ(modemmanager::kModemManagerSMSInterface, method_call->GetInterface()); EXPECT_EQ(modemmanager::kSMSDeleteFunction, method_call->GetMember()); uint32 index = 0; dbus::MessageReader reader(method_call); EXPECT_TRUE(reader.PopUint32(&index)); EXPECT_EQ(expected_index_, index); EXPECT_FALSE(reader.HasMoreData()); message_loop_.task_runner()->PostTask(FROM_HERE, base::Bind(callback, response_)); } // Handles Get method call. void OnGet(dbus::MethodCall* method_call, int timeout_ms, const dbus::ObjectProxy::ResponseCallback& callback) { EXPECT_EQ(modemmanager::kModemManagerSMSInterface, method_call->GetInterface()); EXPECT_EQ(modemmanager::kSMSGetFunction, method_call->GetMember()); uint32 index = 0; dbus::MessageReader reader(method_call); EXPECT_TRUE(reader.PopUint32(&index)); EXPECT_EQ(expected_index_, index); EXPECT_FALSE(reader.HasMoreData()); message_loop_.task_runner()->PostTask(FROM_HERE, base::Bind(callback, response_)); } // Handles List method call. void OnList(dbus::MethodCall* method_call, int timeout_ms, const dbus::ObjectProxy::ResponseCallback& callback) { EXPECT_EQ(modemmanager::kModemManagerSMSInterface, method_call->GetInterface()); EXPECT_EQ(modemmanager::kSMSListFunction, method_call->GetMember()); dbus::MessageReader reader(method_call); EXPECT_FALSE(reader.HasMoreData()); message_loop_.task_runner()->PostTask(FROM_HERE, base::Bind(callback, response_)); } // Checks the results of Get and List. void CheckResult(const base::Value& result) { EXPECT_TRUE(result.Equals(expected_result_)); } protected: // The client to be tested. scoped_ptr client_; // A message loop to emulate asynchronous behavior. base::MessageLoop message_loop_; // The mock bus. scoped_refptr mock_bus_; // The mock object proxy. scoped_refptr mock_proxy_; // The SmsReceived signal handler given by the tested client. dbus::ObjectProxy::SignalCallback sms_received_callback_; // Expected argument for Delete and Get methods. uint32 expected_index_; // Response returned by mock methods. dbus::Response* response_; // Expected result of Get and List methods. base::Value* expected_result_; private: // Used to implement the mock proxy. void OnConnectToSignal( const std::string& interface_name, const std::string& signal_name, const dbus::ObjectProxy::SignalCallback& signal_callback, const dbus::ObjectProxy::OnConnectedCallback& on_connected_callback) { sms_received_callback_ = signal_callback; const bool success = true; message_loop_.task_runner()->PostTask( FROM_HERE, base::Bind(on_connected_callback, interface_name, signal_name, success)); } }; TEST_F(GsmSMSClientTest, SmsReceived) { // Set expectations. const uint32 kIndex = 42; const bool kComplete = true; MockSmsReceivedHandler handler; EXPECT_CALL(handler, Run(kIndex, kComplete)).Times(1); // Set handler. client_->SetSmsReceivedHandler(kServiceName, dbus::ObjectPath(kObjectPath), base::Bind(&MockSmsReceivedHandler::Run, base::Unretained(&handler))); // Run the message loop to run the signal connection result callback. message_loop_.RunUntilIdle(); // Send signal. dbus::Signal signal(modemmanager::kModemManagerSMSInterface, modemmanager::kSMSReceivedSignal); dbus::MessageWriter writer(&signal); writer.AppendUint32(kIndex); writer.AppendBool(kComplete); ASSERT_FALSE(sms_received_callback_.is_null()); sms_received_callback_.Run(&signal); // Reset handler. client_->ResetSmsReceivedHandler(kServiceName, dbus::ObjectPath(kObjectPath)); // Send signal again. sms_received_callback_.Run(&signal); } TEST_F(GsmSMSClientTest, Delete) { // Set expectations. const uint32 kIndex = 42; expected_index_ = kIndex; EXPECT_CALL(*mock_proxy_.get(), CallMethod(_, _, _)) .WillOnce(Invoke(this, &GsmSMSClientTest::OnDelete)); MockDeleteCallback callback; EXPECT_CALL(callback, Run()).Times(1); // Create response. scoped_ptr response(dbus::Response::CreateEmpty()); response_ = response.get(); // Call Delete. client_->Delete(kServiceName, dbus::ObjectPath(kObjectPath), kIndex, base::Bind(&MockDeleteCallback::Run, base::Unretained(&callback))); // Run the message loop. message_loop_.RunUntilIdle(); } TEST_F(GsmSMSClientTest, Get) { // Set expectations. const uint32 kIndex = 42; expected_index_ = kIndex; EXPECT_CALL(*mock_proxy_.get(), CallMethod(_, _, _)) .WillOnce(Invoke(this, &GsmSMSClientTest::OnGet)); MockGetCallback callback; EXPECT_CALL(callback, Run(_)) .WillOnce(Invoke(this, &GsmSMSClientTest::CheckResult)); // Create response. scoped_ptr response(dbus::Response::CreateEmpty()); dbus::MessageWriter writer(response.get()); dbus::MessageWriter array_writer(NULL); writer.OpenArray("{sv}", &array_writer); dbus::MessageWriter entry_writer(NULL); array_writer.OpenDictEntry(&entry_writer); entry_writer.AppendString(kNumberKey); entry_writer.AppendVariantOfString(kExampleNumber); array_writer.CloseContainer(&entry_writer); array_writer.OpenDictEntry(&entry_writer); entry_writer.AppendString(kTextKey); entry_writer.AppendVariantOfString(kExampleText); array_writer.CloseContainer(&entry_writer); writer.CloseContainer(&array_writer); response_ = response.get(); // Create expected result. base::DictionaryValue expected_result; expected_result.SetWithoutPathExpansion( kNumberKey, new base::StringValue(kExampleNumber)); expected_result.SetWithoutPathExpansion(kTextKey, new base::StringValue(kExampleText)); expected_result_ = &expected_result; // Call Delete. client_->Get(kServiceName, dbus::ObjectPath(kObjectPath), kIndex, base::Bind(&MockGetCallback::Run, base::Unretained(&callback))); // Run the message loop. message_loop_.RunUntilIdle(); } TEST_F(GsmSMSClientTest, List) { // Set expectations. EXPECT_CALL(*mock_proxy_.get(), CallMethod(_, _, _)) .WillOnce(Invoke(this, &GsmSMSClientTest::OnList)); MockListCallback callback; EXPECT_CALL(callback, Run(_)) .WillOnce(Invoke(this, &GsmSMSClientTest::CheckResult)); // Create response. scoped_ptr response(dbus::Response::CreateEmpty()); dbus::MessageWriter writer(response.get()); dbus::MessageWriter array_writer(NULL); writer.OpenArray("a{sv}", &array_writer); dbus::MessageWriter sub_array_writer(NULL); array_writer.OpenArray("{sv}", &sub_array_writer); dbus::MessageWriter entry_writer(NULL); sub_array_writer.OpenDictEntry(&entry_writer); entry_writer.AppendString(kNumberKey); entry_writer.AppendVariantOfString(kExampleNumber); sub_array_writer.CloseContainer(&entry_writer); sub_array_writer.OpenDictEntry(&entry_writer); entry_writer.AppendString(kTextKey); entry_writer.AppendVariantOfString(kExampleText); sub_array_writer.CloseContainer(&entry_writer); array_writer.CloseContainer(&sub_array_writer); writer.CloseContainer(&array_writer); response_ = response.get(); // Create expected result. base::ListValue expected_result; base::DictionaryValue* sms = new base::DictionaryValue; sms->SetWithoutPathExpansion(kNumberKey, new base::StringValue(kExampleNumber)); sms->SetWithoutPathExpansion(kTextKey, new base::StringValue(kExampleText)); expected_result.Append(sms); expected_result_ = &expected_result; // Call List. client_->List(kServiceName, dbus::ObjectPath(kObjectPath), base::Bind(&MockListCallback::Run, base::Unretained(&callback))); // Run the message loop. message_loop_.RunUntilIdle(); } } // namespace chromeos