1 // Copyright 2014 The Chromium OS 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 // Helper utilities to simplify testing of D-Bus object implementations.
6 // Since the method handlers could now be asynchronous, they use callbacks to
7 // provide method return values. This makes it really difficult to invoke
8 // such handlers in unit tests (even if they are actually synchronous but
9 // still use DBusMethodResponse to send back the method results).
10 // This file provide testing-only helpers to make calling D-Bus method handlers
11 // easier.
12 #ifndef LIBBRILLO_BRILLO_DBUS_DBUS_OBJECT_TEST_HELPERS_H_
13 #define LIBBRILLO_BRILLO_DBUS_DBUS_OBJECT_TEST_HELPERS_H_
14
15 #include <base/bind.h>
16 #include <base/memory/weak_ptr.h>
17 #include <brillo/dbus/dbus_method_invoker.h>
18 #include <brillo/dbus/dbus_object.h>
19
20 namespace brillo {
21 namespace dbus_utils {
22
23 // Helper friend class to call DBusInterface::HandleMethodCall() since it is
24 // a private method of the class and we don't want to make it public.
25 class DBusInterfaceTestHelper final {
26 public:
HandleMethodCall(DBusInterface * itf,dbus::MethodCall * method_call,ResponseSender sender)27 static void HandleMethodCall(DBusInterface* itf,
28 dbus::MethodCall* method_call,
29 ResponseSender sender) {
30 itf->HandleMethodCall(method_call, sender);
31 }
32 };
33
34 namespace testing {
35
36 // This is a simple class that has weak pointer semantics and holds an
37 // instance of D-Bus method call response message. We use this in tests
38 // to get the response in case the handler processes a method call request
39 // synchronously. Otherwise the ResponseHolder object will be destroyed and
40 // ResponseHolder::ReceiveResponse() will not be called since we bind the
41 // callback to the object instance via a weak pointer.
42 struct ResponseHolder final : public base::SupportsWeakPtr<ResponseHolder> {
ReceiveResponsefinal43 void ReceiveResponse(scoped_ptr<dbus::Response> response) {
44 response_.reset(response.release());
45 }
46
47 std::unique_ptr<dbus::Response> response_;
48 };
49
50 // Dispatches a D-Bus method call to the corresponding handler.
51 // Used mostly for testing purposes. This method is inlined so that it is
52 // not included in the shipping code of libbrillo, and included at the
53 // call sites. Returns a response from the method handler or nullptr if the
54 // method hasn't provided the response message immediately
55 // (i.e. it is asynchronous).
CallMethod(const DBusObject & object,dbus::MethodCall * method_call)56 inline std::unique_ptr<dbus::Response> CallMethod(
57 const DBusObject& object, dbus::MethodCall* method_call) {
58 DBusInterface* itf = object.FindInterface(method_call->GetInterface());
59 std::unique_ptr<dbus::Response> response;
60 if (!itf) {
61 response = CreateDBusErrorResponse(
62 method_call,
63 DBUS_ERROR_UNKNOWN_INTERFACE,
64 "Interface you invoked a method on isn't known by the object.");
65 } else {
66 ResponseHolder response_holder;
67 DBusInterfaceTestHelper::HandleMethodCall(
68 itf, method_call, base::Bind(&ResponseHolder::ReceiveResponse,
69 response_holder.AsWeakPtr()));
70 response = std::move(response_holder.response_);
71 }
72 return response;
73 }
74
75 // MethodHandlerInvoker is similar to CallMethod() function above, except
76 // it allows the callers to invoke the method handlers directly bypassing
77 // the DBusObject/DBusInterface infrastructure.
78 // This works only on synchronous methods though. The handler must reply
79 // before the handler exits.
80 template<typename RetType>
81 struct MethodHandlerInvoker {
82 // MethodHandlerInvoker<RetType>::Call() calls a member |method| of a class
83 // |instance| and passes the |args| to it. The method's return value provided
84 // via handler's DBusMethodResponse is then extracted and returned.
85 // If the method handler returns an error, the error information is passed
86 // to the caller via the |error| object (and the method returns a default
87 // value of type RetType as a placeholder).
88 // If the method handler asynchronous and did not provide a reply (success or
89 // error) before the handler exits, this method aborts with a CHECK().
90 template<class Class, typename... Params, typename... Args>
CallMethodHandlerInvoker91 static RetType Call(
92 ErrorPtr* error,
93 Class* instance,
94 void(Class::*method)(std::unique_ptr<DBusMethodResponse<RetType>>,
95 Params...),
96 Args... args) {
97 ResponseHolder response_holder;
98 dbus::MethodCall method_call("test.interface", "TestMethod");
99 method_call.SetSerial(123);
100 std::unique_ptr<DBusMethodResponse<RetType>> method_response{
101 new DBusMethodResponse<RetType>(
102 &method_call, base::Bind(&ResponseHolder::ReceiveResponse,
103 response_holder.AsWeakPtr()))
104 };
105 (instance->*method)(std::move(method_response), args...);
106 CHECK(response_holder.response_.get())
107 << "No response received. Asynchronous methods are not supported.";
108 RetType ret_val;
109 ExtractMethodCallResults(response_holder.response_.get(), error, &ret_val);
110 return ret_val;
111 }
112 };
113
114 // Specialization of MethodHandlerInvoker for methods that do not return
115 // values (void methods).
116 template<>
117 struct MethodHandlerInvoker<void> {
118 template<class Class, typename... Params, typename... Args>
119 static void Call(
120 ErrorPtr* error,
121 Class* instance,
122 void(Class::*method)(std::unique_ptr<DBusMethodResponse<>>, Params...),
123 Args... args) {
124 ResponseHolder response_holder;
125 dbus::MethodCall method_call("test.interface", "TestMethod");
126 method_call.SetSerial(123);
127 std::unique_ptr<DBusMethodResponse<>> method_response{
128 new DBusMethodResponse<>(&method_call,
129 base::Bind(&ResponseHolder::ReceiveResponse,
130 response_holder.AsWeakPtr()))
131 };
132 (instance->*method)(std::move(method_response), args...);
133 CHECK(response_holder.response_.get())
134 << "No response received. Asynchronous methods are not supported.";
135 ExtractMethodCallResults(response_holder.response_.get(), error);
136 }
137 };
138
139 } // namespace testing
140 } // namespace dbus_utils
141 } // namespace brillo
142
143 #endif // LIBBRILLO_BRILLO_DBUS_DBUS_OBJECT_TEST_HELPERS_H_
144