diff options
author | satorux@chromium.org <satorux@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-08-25 05:18:29 +0000 |
---|---|---|
committer | satorux@chromium.org <satorux@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-08-25 05:18:29 +0000 |
commit | b8ae051ea8edaefb9f787bdca704e2df1adfb89f (patch) | |
tree | f26c2509c5f31de44d9446d17be64666558bdfcc | |
parent | 2517cfa5066e63e6ed2642779ea57616ae49eb42 (diff) | |
download | chromium_src-b8ae051ea8edaefb9f787bdca704e2df1adfb89f.zip chromium_src-b8ae051ea8edaefb9f787bdca704e2df1adfb89f.tar.gz chromium_src-b8ae051ea8edaefb9f787bdca704e2df1adfb89f.tar.bz2 |
Add mock classes for Bus, ObjectProxy, and ExportedObject.
Also add mock_unittest.cc that demonstrates how to mock synchronos and
asynchronos D-Bus method calls.
BUG=90036
TEST=dbus_unittests
Review URL: http://codereview.chromium.org/7714030
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@98188 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | dbus/bus.h | 5 | ||||
-rw-r--r-- | dbus/dbus.gyp | 31 | ||||
-rw-r--r-- | dbus/exported_object.h | 5 | ||||
-rw-r--r-- | dbus/mock_bus.cc | 15 | ||||
-rw-r--r-- | dbus/mock_bus.h | 71 | ||||
-rw-r--r-- | dbus/mock_exported_object.cc | 18 | ||||
-rw-r--r-- | dbus/mock_exported_object.h | 39 | ||||
-rw-r--r-- | dbus/mock_object_proxy.cc | 18 | ||||
-rw-r--r-- | dbus/mock_object_proxy.h | 39 | ||||
-rw-r--r-- | dbus/mock_unittest.cc | 171 | ||||
-rw-r--r-- | dbus/object_proxy.h | 6 |
11 files changed, 411 insertions, 7 deletions
@@ -368,9 +368,12 @@ class Bus : public base::RefCountedThreadSafe<Bus> { // AssertOnOriginThread(). virtual void AssertOnDBusThread(); + protected: + // This is protected, so we can define sub classes. + virtual ~Bus(); + private: friend class base::RefCountedThreadSafe<Bus>; - virtual ~Bus(); // Helper function used for Shutdown(). void ShutdownInternal(OnShutdownCallback callback); diff --git a/dbus/dbus.gyp b/dbus/dbus.gyp index 143f672..c17aecf 100644 --- a/dbus/dbus.gyp +++ b/dbus/dbus.gyp @@ -17,8 +17,8 @@ 'sources': [ 'bus.cc', 'bus.h', - 'exported_object.h', 'exported_object.cc', + 'exported_object.h', 'message.cc', 'message.h', 'object_proxy.cc', @@ -27,20 +27,43 @@ ], }, { + # This target contains mocks that can be used to write unit tests + # without issuing actual D-Bus calls. + 'target_name': 'dbus_test_support', + 'type': 'static_library', + 'dependencies': [ + '../build/linux/system.gyp:dbus', + '../testing/gmock.gyp:gmock', + 'dbus', + ], + 'sources': [ + 'mock_bus.cc', + 'mock_bus.h', + 'mock_object_proxy.cc', + 'mock_object_proxy.h', + ], + 'include_dirs': [ + '..', + ], + }, + { 'target_name': 'dbus_unittests', 'type': 'executable', 'dependencies': [ - 'dbus', '../base/base.gyp:test_support_base', - '../testing/gtest.gyp:gtest', '../build/linux/system.gyp:dbus', + '../testing/gmock.gyp:gmock', + '../testing/gtest.gyp:gtest', + 'dbus', + 'dbus_test_support', ], 'sources': [ '../base/test/run_all_unittests.cc', 'bus_unittest.cc', - 'message_unittest.cc', 'end_to_end_async_unittest.cc', 'end_to_end_sync_unittest.cc', + 'message_unittest.cc', + 'mock_unittest.cc', 'test_service.cc', 'test_service.h', ], diff --git a/dbus/exported_object.h b/dbus/exported_object.h index 1351824..d9cda57 100644 --- a/dbus/exported_object.h +++ b/dbus/exported_object.h @@ -86,9 +86,12 @@ class ExportedObject : public base::RefCountedThreadSafe<ExportedObject> { // BLOCKING CALL. virtual void Unregister(); + protected: + // This is protected, so we can define sub classes. + virtual ~ExportedObject(); + private: friend class base::RefCountedThreadSafe<ExportedObject>; - virtual ~ExportedObject(); // Helper function for ExportMethod(). void ExportMethodInternal(const std::string& interface_name, diff --git a/dbus/mock_bus.cc b/dbus/mock_bus.cc new file mode 100644 index 0000000..e97cd71 --- /dev/null +++ b/dbus/mock_bus.cc @@ -0,0 +1,15 @@ +// Copyright (c) 2011 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 "dbus/mock_bus.h" + +namespace dbus { + +MockBus::MockBus(Bus::Options& options) : Bus(options) { +} + +MockBus::~MockBus() { +} + +} // namespace dbus diff --git a/dbus/mock_bus.h b/dbus/mock_bus.h new file mode 100644 index 0000000..f1c5b4e --- /dev/null +++ b/dbus/mock_bus.h @@ -0,0 +1,71 @@ +// Copyright (c) 2011 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. + +#ifndef DBUS_MOCK_BUS_H_ +#define DBUS_MOCK_BUS_H_ +#pragma once + +#include "dbus/bus.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace dbus { + +// Mock for Bus class. Along with MockObjectProxy and MockExportedObject, +// the mock classes can be used to write unit tests without issuing real +// D-Bus calls. +class MockBus : public Bus { + public: + MockBus(Bus::Options& options); + virtual ~MockBus(); + + MOCK_METHOD2(GetObjectProxy, ObjectProxy*(const std::string& service_name, + const std::string& object_path)); + MOCK_METHOD2(GetExportedObject, ExportedObject*( + const std::string& service_name, + const std::string& object_path)); + MOCK_METHOD0(ShutdownAndBlock, void()); + MOCK_METHOD1(Shutdown, void(OnShutdownCallback callback)); + MOCK_METHOD0(Connect, bool()); + MOCK_METHOD1(RequestOwnership, bool(const std::string& service_name)); + MOCK_METHOD1(ReleaseOwnership, bool(const std::string& service_name)); + MOCK_METHOD0(SetUpAsyncOperations, bool()); + MOCK_METHOD3(SendWithReplyAndBlock, DBusMessage*(DBusMessage* request, + int timeout_ms, + DBusError* error)); + MOCK_METHOD3(SendWithReply, void(DBusMessage* request, + DBusPendingCall** pending_call, + int timeout_ms)); + MOCK_METHOD2(Send, void(DBusMessage* request, + uint32* serial)); + MOCK_METHOD2(AddFilter, void(DBusHandleMessageFunction handle_message, + void* user_data)); + MOCK_METHOD2(RemoveFilter, void(DBusHandleMessageFunction handle_message, + void* user_data)); + MOCK_METHOD2(AddMatch, void(const std::string& match_rule, + DBusError* error)); + MOCK_METHOD2(RemoveMatch, void(const std::string& match_rule, + DBusError* error)); + MOCK_METHOD4(TryRegisterObjectPath, bool(const std::string& object_path, + const DBusObjectPathVTable* vtable, + void* user_data, + DBusError* error)); + MOCK_METHOD1(UnregisterObjectPath, void(const std::string& object_path)); + MOCK_METHOD2(PostTaskToOriginThread, void( + const tracked_objects::Location& from_here, + const base::Closure& task)); + MOCK_METHOD2(PostTaskToDBusThread, void( + const tracked_objects::Location& from_here, + const base::Closure& task)); + MOCK_METHOD3(PostDelayedTaskToDBusThread, void( + const tracked_objects::Location& from_here, + const base::Closure& task, + int delay_ms)); + MOCK_METHOD0(HasDBusThread, bool()); + MOCK_METHOD0(AssertOnOriginThread, void()); + MOCK_METHOD0(AssertOnDBusThread, void()); +}; + +} // namespace dbus + +#endif // DBUS_MOCK_BUS_H_ diff --git a/dbus/mock_exported_object.cc b/dbus/mock_exported_object.cc new file mode 100644 index 0000000..0fd4f2e --- /dev/null +++ b/dbus/mock_exported_object.cc @@ -0,0 +1,18 @@ +// Copyright (c) 2011 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 "dbus/mock_exported_object.h" + +namespace dbus { + +MockExportedObject::MockExportedObject(Bus* bus, + const std::string& service_name, + const std::string& object_path) + : ExportedObject(bus, service_name, object_path) { +} + +MockExportedObject::~MockExportedObject() { +} + +} // namespace dbus diff --git a/dbus/mock_exported_object.h b/dbus/mock_exported_object.h new file mode 100644 index 0000000..17a36d0 --- /dev/null +++ b/dbus/mock_exported_object.h @@ -0,0 +1,39 @@ +// Copyright (c) 2011 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. + +#ifndef DBUS_MOCK_EXPORTED_OBJECT_H_ +#define DBUS_MOCK_EXPORTED_OBJECT_H_ +#pragma once + +#include <string> + +#include "dbus/exported_object.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace dbus { + +// Mock for ExportedObject. +class MockExportedObject : public ExportedObject { + public: + MockExportedObject(Bus* bus, + const std::string& service_name, + const std::string& object_path); + virtual ~MockExportedObject(); + + MOCK_METHOD3(ExportMethodAndBlock, + bool(const std::string& interface_name, + const std::string& method_name, + MethodCallCallback method_call_callback)); + MOCK_METHOD4(ExportMethod, + void(const std::string& interface_name, + const std::string& method_name, + MethodCallCallback method_call_callback, + OnExportedCallback on_exported_callback)); + MOCK_METHOD1(SendSignal, void(Signal* signal)); + MOCK_METHOD0(Unregister, void()); +}; + +} // namespace dbus + +#endif // DBUS_MOCK_EXPORTED_OBJECT_H_ diff --git a/dbus/mock_object_proxy.cc b/dbus/mock_object_proxy.cc new file mode 100644 index 0000000..d58a604 --- /dev/null +++ b/dbus/mock_object_proxy.cc @@ -0,0 +1,18 @@ +// Copyright (c) 2011 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 "dbus/mock_object_proxy.h" + +namespace dbus { + +MockObjectProxy::MockObjectProxy(Bus* bus, + const std::string& service_name, + const std::string& object_path) + : ObjectProxy(bus, service_name, object_path) { +} + +MockObjectProxy::~MockObjectProxy() { +} + +} // namespace dbus diff --git a/dbus/mock_object_proxy.h b/dbus/mock_object_proxy.h new file mode 100644 index 0000000..b5d4477 --- /dev/null +++ b/dbus/mock_object_proxy.h @@ -0,0 +1,39 @@ +// Copyright (c) 2011 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. + +#ifndef DBUS_MOCK_OBJECT_PROXY_H_ +#define DBUS_MOCK_OBJECT_PROXY_H_ +#pragma once + +#include <string> + +#include "dbus/object_proxy.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace dbus { + +// Mock for ObjectProxy. +class MockObjectProxy : public ObjectProxy { + public: + MockObjectProxy(Bus* bus, + const std::string& service_name, + const std::string& object_path); + virtual ~MockObjectProxy(); + + MOCK_METHOD2(CallMethodAndBlock, Response*(MethodCall* method_call, + int timeout_ms)); + MOCK_METHOD3(CallMethod, void(MethodCall* method_call, + int timeout_ms, + ResponseCallback callback)); + MOCK_METHOD4(ConnectToSignal, + void(const std::string& interface_name, + const std::string& signal_name, + SignalCallback signal_callback, + OnConnectedCallback on_connected_callback)); + MOCK_METHOD0(Detach, void()); +}; + +} // namespace dbus + +#endif // DBUS_MOCK_OBJECT_PROXY_H_ diff --git a/dbus/mock_unittest.cc b/dbus/mock_unittest.cc new file mode 100644 index 0000000..f0289cf --- /dev/null +++ b/dbus/mock_unittest.cc @@ -0,0 +1,171 @@ +// Copyright (c) 2011 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 "base/bind.h" +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "dbus/message.h" +#include "dbus/mock_bus.h" +#include "dbus/mock_object_proxy.h" +#include "dbus/mock_exported_object.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::_; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::Unused; + +class MockTest : public testing::Test { + public: + MockTest() { + } + + void SetUp() { + // 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(), + "org.chromium.TestService", + "/org/chromium/TestObject"); + + // Set an expectation so mock_proxy's CallMethodAndBlock() will use + // CreateMockProxyResponse() to return responses. + EXPECT_CALL(*mock_proxy_, CallMethodAndBlock(_, _)) + .WillRepeatedly(Invoke(this, &MockTest::CreateMockProxyResponse)); + + // Set an expectation so mock_proxy's CallMethod() will use + // HandleMockProxyResponseWithMessageLoop() to return responses. + EXPECT_CALL(*mock_proxy_, CallMethod(_, _, _)) + .WillRepeatedly( + Invoke(this, + &MockTest::HandleMockProxyResponseWithMessageLoop)); + + // 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_, GetObjectProxy("org.chromium.TestService", + "/org/chromium/TestObject")) + .WillOnce(Return(mock_proxy_.get())); + } + + // Called when the response is received. + void OnResponse(dbus::Response* response) { + // |response| will be deleted on exit of the function. Copy the + // payload to |response_string_|. + if (response) { + dbus::MessageReader reader(response); + ASSERT_TRUE(reader.PopString(&response_string_)); + } + message_loop_.Quit(); + }; + + protected: + std::string response_string_; + MessageLoop message_loop_; + scoped_refptr<dbus::MockBus> mock_bus_; + scoped_refptr<dbus::MockObjectProxy> mock_proxy_; + + private: + // Returns a response for the given method call. Used to implement + // CallMethodAndBlock() for |mock_proxy_|. + dbus::Response* CreateMockProxyResponse(dbus::MethodCall* method_call, + int timeout_ms) { + if (method_call->GetInterface() == "org.chromium.TestInterface" && + method_call->GetMember() == "Echo") { + dbus::MessageReader reader(method_call); + std::string text_message; + if (reader.PopString(&text_message)) { + dbus::Response* response = dbus::Response::CreateEmpty(); + dbus::MessageWriter writer(response); + writer.AppendString(text_message); + return response; + } + } + + LOG(ERROR) << "Unexpected method call: " << method_call->ToString(); + return NULL; + } + + // Creates a response and runs the given response callback in the + // message loop with the response. Used to implement for |mock_proxy_|. + void HandleMockProxyResponseWithMessageLoop( + dbus::MethodCall* method_call, + int timeout_ms, + dbus::ObjectProxy::ResponseCallback response_callback) { + dbus::Response* response = CreateMockProxyResponse(method_call, + timeout_ms); + message_loop_.PostTask(FROM_HERE, + base::Bind(&MockTest::RunResponseCallback, + base::Unretained(this), + response_callback, + response)); + } + + // Runs the given response callback with the given response. + void RunResponseCallback( + dbus::ObjectProxy::ResponseCallback response_callback, + dbus::Response* response) { + response_callback.Run(response); + delete response; + } +}; + +// This test demonstrates how to mock a synchronos method call using the +// mock classes. +TEST_F(MockTest, CallMethodAndBlock) { + const char kHello[] = "Hello"; + // Get an object proxy from the mock bus. + dbus::ObjectProxy* proxy = mock_bus_->GetObjectProxy( + "org.chromium.TestService", + "/org/chromium/TestObject"); + + // Create a method call. + dbus::MethodCall method_call("org.chromium.TestInterface", "Echo"); + dbus::MessageWriter writer(&method_call); + writer.AppendString(kHello); + + // Call the method. + scoped_ptr<dbus::Response> response( + proxy->CallMethodAndBlock(&method_call, + dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); + + // Check the response. + ASSERT_TRUE(response.get()); + dbus::MessageReader reader(response.get()); + std::string text_message; + ASSERT_TRUE(reader.PopString(&text_message)); + // The text message should be echo'ed back. + EXPECT_EQ(kHello, text_message); +} + +// This test demonstrates how to mock an asynchronos method call using the +// mock classes. +TEST_F(MockTest, CallMethod) { + const char kHello[] = "hello"; + + // Get an object proxy from the mock bus. + dbus::ObjectProxy* proxy = mock_bus_->GetObjectProxy( + "org.chromium.TestService", + "/org/chromium/TestObject"); + + // Create a method call. + dbus::MethodCall method_call("org.chromium.TestInterface", "Echo"); + dbus::MessageWriter writer(&method_call); + writer.AppendString(kHello); + + // Call the method. + proxy->CallMethod(&method_call, + dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&MockTest::OnResponse, + base::Unretained(this))); + // Run the message loop to let OnResponse be called. + message_loop_.Run(); + + EXPECT_EQ(kHello, response_string_); +} diff --git a/dbus/object_proxy.h b/dbus/object_proxy.h index ae9bd9a3..f75e330 100644 --- a/dbus/object_proxy.h +++ b/dbus/object_proxy.h @@ -6,6 +6,7 @@ #define DBUS_OBJECT_PROXY_H_ #pragma once +#include <map> #include <string> #include <vector> #include <dbus/dbus.h> @@ -104,9 +105,12 @@ class ObjectProxy : public base::RefCountedThreadSafe<ObjectProxy> { // BLOCKING CALL. virtual void Detach(); + protected: + // This is protected, so we can define sub classes. + virtual ~ObjectProxy(); + private: friend class base::RefCountedThreadSafe<ObjectProxy>; - virtual ~ObjectProxy(); // Struct of data we'll be passing from StartAsyncMethodCall() to // OnPendingCallIsCompleteThunk(). |