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 "base/bind.h" 6#include "base/logging.h" 7#include "base/memory/ref_counted.h" 8#include "base/memory/scoped_ptr.h" 9#include "base/message_loop/message_loop.h" 10#include "base/run_loop.h" 11#include "dbus/message.h" 12#include "dbus/mock_bus.h" 13#include "dbus/mock_exported_object.h" 14#include "dbus/mock_object_proxy.h" 15#include "dbus/object_path.h" 16#include "dbus/scoped_dbus_error.h" 17#include "testing/gmock/include/gmock/gmock.h" 18#include "testing/gtest/include/gtest/gtest.h" 19 20using ::testing::_; 21using ::testing::Invoke; 22using ::testing::Return; 23using ::testing::Unused; 24 25namespace dbus { 26 27class MockTest : public testing::Test { 28 public: 29 MockTest() { 30 } 31 32 virtual void SetUp() { 33 // Create a mock bus. 34 Bus::Options options; 35 options.bus_type = Bus::SYSTEM; 36 mock_bus_ = new MockBus(options); 37 38 // Create a mock proxy. 39 mock_proxy_ = new MockObjectProxy( 40 mock_bus_.get(), 41 "org.chromium.TestService", 42 ObjectPath("/org/chromium/TestObject")); 43 44 // Set an expectation so mock_proxy's CallMethodAndBlock() will use 45 // CreateMockProxyResponse() to return responses. 46 EXPECT_CALL(*mock_proxy_.get(), MockCallMethodAndBlock(_, _)) 47 .WillRepeatedly(Invoke(this, &MockTest::CreateMockProxyResponse)); 48 EXPECT_CALL(*mock_proxy_.get(), 49 MockCallMethodAndBlockWithErrorDetails(_, _, _)) 50 .WillRepeatedly( 51 Invoke(this, &MockTest::CreateMockProxyResponseWithErrorDetails)); 52 53 // Set an expectation so mock_proxy's CallMethod() will use 54 // HandleMockProxyResponseWithMessageLoop() to return responses. 55 EXPECT_CALL(*mock_proxy_.get(), CallMethod(_, _, _)).WillRepeatedly( 56 Invoke(this, &MockTest::HandleMockProxyResponseWithMessageLoop)); 57 58 // Set an expectation so mock_bus's GetObjectProxy() for the given 59 // service name and the object path will return mock_proxy_. 60 EXPECT_CALL(*mock_bus_.get(), 61 GetObjectProxy("org.chromium.TestService", 62 ObjectPath("/org/chromium/TestObject"))) 63 .WillOnce(Return(mock_proxy_.get())); 64 65 // ShutdownAndBlock() will be called in TearDown(). 66 EXPECT_CALL(*mock_bus_.get(), ShutdownAndBlock()).WillOnce(Return()); 67 } 68 69 virtual void TearDown() { 70 mock_bus_->ShutdownAndBlock(); 71 } 72 73 // Called when the response is received. 74 void OnResponse(Response* response) { 75 // |response| will be deleted on exit of the function. Copy the 76 // payload to |response_string_|. 77 if (response) { 78 MessageReader reader(response); 79 ASSERT_TRUE(reader.PopString(&response_string_)); 80 } 81 run_loop_->Quit(); 82 }; 83 84 protected: 85 std::string response_string_; 86 base::MessageLoop message_loop_; 87 scoped_ptr<base::RunLoop> run_loop_; 88 scoped_refptr<MockBus> mock_bus_; 89 scoped_refptr<MockObjectProxy> mock_proxy_; 90 91 private: 92 // Returns a response for the given method call. Used to implement 93 // CallMethodAndBlock() for |mock_proxy_|. 94 Response* CreateMockProxyResponse(MethodCall* method_call, 95 int timeout_ms) { 96 if (method_call->GetInterface() == "org.chromium.TestInterface" && 97 method_call->GetMember() == "Echo") { 98 MessageReader reader(method_call); 99 std::string text_message; 100 if (reader.PopString(&text_message)) { 101 scoped_ptr<Response> response = Response::CreateEmpty(); 102 MessageWriter writer(response.get()); 103 writer.AppendString(text_message); 104 return response.release(); 105 } 106 } 107 108 LOG(ERROR) << "Unexpected method call: " << method_call->ToString(); 109 return NULL; 110 } 111 112 Response* CreateMockProxyResponseWithErrorDetails( 113 MethodCall* method_call, int timeout_ms, ScopedDBusError* error) { 114 dbus_set_error(error->get(), DBUS_ERROR_NOT_SUPPORTED, "Not implemented"); 115 return NULL; 116 } 117 118 // Creates a response and runs the given response callback in the 119 // message loop with the response. Used to implement for |mock_proxy_|. 120 void HandleMockProxyResponseWithMessageLoop( 121 MethodCall* method_call, 122 int timeout_ms, 123 ObjectProxy::ResponseCallback response_callback) { 124 Response* response = CreateMockProxyResponse(method_call, timeout_ms); 125 message_loop_.PostTask(FROM_HERE, 126 base::Bind(&MockTest::RunResponseCallback, 127 base::Unretained(this), 128 response_callback, 129 response)); 130 } 131 132 // Runs the given response callback with the given response. 133 void RunResponseCallback( 134 ObjectProxy::ResponseCallback response_callback, 135 Response* response) { 136 response_callback.Run(response); 137 delete response; 138 } 139}; 140 141// This test demonstrates how to mock a synchronous method call using the 142// mock classes. 143TEST_F(MockTest, CallMethodAndBlock) { 144 const char kHello[] = "Hello"; 145 // Get an object proxy from the mock bus. 146 ObjectProxy* proxy = mock_bus_->GetObjectProxy( 147 "org.chromium.TestService", 148 ObjectPath("/org/chromium/TestObject")); 149 150 // Create a method call. 151 MethodCall method_call("org.chromium.TestInterface", "Echo"); 152 MessageWriter writer(&method_call); 153 writer.AppendString(kHello); 154 155 // Call the method. 156 scoped_ptr<Response> response( 157 proxy->CallMethodAndBlock(&method_call, 158 ObjectProxy::TIMEOUT_USE_DEFAULT)); 159 160 // Check the response. 161 ASSERT_TRUE(response.get()); 162 MessageReader reader(response.get()); 163 std::string text_message; 164 ASSERT_TRUE(reader.PopString(&text_message)); 165 // The text message should be echo'ed back. 166 EXPECT_EQ(kHello, text_message); 167} 168 169TEST_F(MockTest, CallMethodAndBlockWithErrorDetails) { 170 // Get an object proxy from the mock bus. 171 ObjectProxy* proxy = mock_bus_->GetObjectProxy( 172 "org.chromium.TestService", 173 ObjectPath("/org/chromium/TestObject")); 174 175 // Create a method call. 176 MethodCall method_call("org.chromium.TestInterface", "Echo"); 177 178 ScopedDBusError error; 179 // Call the method. 180 scoped_ptr<Response> response( 181 proxy->CallMethodAndBlockWithErrorDetails( 182 &method_call, ObjectProxy::TIMEOUT_USE_DEFAULT, &error)); 183 184 // Check the response. 185 ASSERT_FALSE(response.get()); 186 ASSERT_TRUE(error.is_set()); 187 EXPECT_STREQ(DBUS_ERROR_NOT_SUPPORTED, error.name()); 188 EXPECT_STREQ("Not implemented", error.message()); 189} 190 191// This test demonstrates how to mock an asynchronous method call using the 192// mock classes. 193TEST_F(MockTest, CallMethod) { 194 const char kHello[] = "hello"; 195 196 // Get an object proxy from the mock bus. 197 ObjectProxy* proxy = mock_bus_->GetObjectProxy( 198 "org.chromium.TestService", 199 ObjectPath("/org/chromium/TestObject")); 200 201 // Create a method call. 202 MethodCall method_call("org.chromium.TestInterface", "Echo"); 203 MessageWriter writer(&method_call); 204 writer.AppendString(kHello); 205 206 // Call the method. 207 run_loop_.reset(new base::RunLoop); 208 proxy->CallMethod(&method_call, 209 ObjectProxy::TIMEOUT_USE_DEFAULT, 210 base::Bind(&MockTest::OnResponse, 211 base::Unretained(this))); 212 // Run the message loop to let OnResponse be called. 213 run_loop_->Run(); 214 215 EXPECT_EQ(kHello, response_string_); 216} 217 218} // namespace dbus 219