You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
147 lines
6.1 KiB
147 lines
6.1 KiB
// Copyright 2014 The Chromium OS Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// Helper utilities to simplify testing of D-Bus object implementations.
|
|
// Since the method handlers could now be asynchronous, they use callbacks to
|
|
// provide method return values. This makes it really difficult to invoke
|
|
// such handlers in unit tests (even if they are actually synchronous but
|
|
// still use DBusMethodResponse to send back the method results).
|
|
// This file provide testing-only helpers to make calling D-Bus method handlers
|
|
// easier.
|
|
#ifndef LIBBRILLO_BRILLO_DBUS_DBUS_OBJECT_TEST_HELPERS_H_
|
|
#define LIBBRILLO_BRILLO_DBUS_DBUS_OBJECT_TEST_HELPERS_H_
|
|
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#include <base/bind.h>
|
|
#include <base/memory/weak_ptr.h>
|
|
#include <brillo/dbus/dbus_method_invoker.h>
|
|
#include <brillo/dbus/dbus_object.h>
|
|
|
|
namespace brillo {
|
|
namespace dbus_utils {
|
|
|
|
// Helper friend class to call DBusInterface::HandleMethodCall() since it is
|
|
// a private method of the class and we don't want to make it public.
|
|
class DBusInterfaceTestHelper final {
|
|
public:
|
|
static void HandleMethodCall(DBusInterface* itf,
|
|
::dbus::MethodCall* method_call,
|
|
ResponseSender sender) {
|
|
itf->HandleMethodCall(method_call, sender);
|
|
}
|
|
};
|
|
|
|
namespace testing {
|
|
|
|
// This is a simple class that has weak pointer semantics and holds an
|
|
// instance of D-Bus method call response message. We use this in tests
|
|
// to get the response in case the handler processes a method call request
|
|
// synchronously. Otherwise the ResponseHolder object will be destroyed and
|
|
// ResponseHolder::ReceiveResponse() will not be called since we bind the
|
|
// callback to the object instance via a weak pointer.
|
|
struct ResponseHolder final : public base::SupportsWeakPtr<ResponseHolder> {
|
|
void ReceiveResponse(std::unique_ptr<::dbus::Response> response) {
|
|
response_ = std::move(response);
|
|
}
|
|
|
|
std::unique_ptr<::dbus::Response> response_;
|
|
};
|
|
|
|
// Dispatches a D-Bus method call to the corresponding handler.
|
|
// Used mostly for testing purposes. This method is inlined so that it is
|
|
// not included in the shipping code of libbrillo, and included at the
|
|
// call sites. Returns a response from the method handler or nullptr if the
|
|
// method hasn't provided the response message immediately
|
|
// (i.e. it is asynchronous).
|
|
inline std::unique_ptr<::dbus::Response> CallMethod(
|
|
const DBusObject& object, ::dbus::MethodCall* method_call) {
|
|
DBusInterface* itf = object.FindInterface(method_call->GetInterface());
|
|
std::unique_ptr<::dbus::Response> response;
|
|
if (!itf) {
|
|
response = CreateDBusErrorResponse(
|
|
method_call,
|
|
DBUS_ERROR_UNKNOWN_INTERFACE,
|
|
"Interface you invoked a method on isn't known by the object.");
|
|
} else {
|
|
ResponseHolder response_holder;
|
|
DBusInterfaceTestHelper::HandleMethodCall(
|
|
itf, method_call, base::Bind(&ResponseHolder::ReceiveResponse,
|
|
response_holder.AsWeakPtr()));
|
|
response = std::move(response_holder.response_);
|
|
}
|
|
return response;
|
|
}
|
|
|
|
// MethodHandlerInvoker is similar to CallMethod() function above, except
|
|
// it allows the callers to invoke the method handlers directly bypassing
|
|
// the DBusObject/DBusInterface infrastructure.
|
|
// This works only on synchronous methods though. The handler must reply
|
|
// before the handler exits.
|
|
template<typename RetType>
|
|
struct MethodHandlerInvoker {
|
|
// MethodHandlerInvoker<RetType>::Call() calls a member |method| of a class
|
|
// |instance| and passes the |args| to it. The method's return value provided
|
|
// via handler's DBusMethodResponse is then extracted and returned.
|
|
// If the method handler returns an error, the error information is passed
|
|
// to the caller via the |error| object (and the method returns a default
|
|
// value of type RetType as a placeholder).
|
|
// If the method handler asynchronous and did not provide a reply (success or
|
|
// error) before the handler exits, this method aborts with a CHECK().
|
|
template<class Class, typename... Params, typename... Args>
|
|
static RetType Call(
|
|
ErrorPtr* error,
|
|
Class* instance,
|
|
void(Class::*method)(std::unique_ptr<DBusMethodResponse<RetType>>,
|
|
Params...),
|
|
Args... args) {
|
|
ResponseHolder response_holder;
|
|
::dbus::MethodCall method_call("test.interface", "TestMethod");
|
|
method_call.SetSerial(123);
|
|
std::unique_ptr<DBusMethodResponse<RetType>> method_response{
|
|
new DBusMethodResponse<RetType>(
|
|
&method_call, base::Bind(&ResponseHolder::ReceiveResponse,
|
|
response_holder.AsWeakPtr()))
|
|
};
|
|
(instance->*method)(std::move(method_response), args...);
|
|
CHECK(response_holder.response_.get())
|
|
<< "No response received. Asynchronous methods are not supported.";
|
|
RetType ret_val;
|
|
ExtractMethodCallResults(response_holder.response_.get(), error, &ret_val);
|
|
return ret_val;
|
|
}
|
|
};
|
|
|
|
// Specialization of MethodHandlerInvoker for methods that do not return
|
|
// values (void methods).
|
|
template<>
|
|
struct MethodHandlerInvoker<void> {
|
|
template<class Class, typename... Params, typename... Args>
|
|
static void Call(
|
|
ErrorPtr* error,
|
|
Class* instance,
|
|
void(Class::*method)(std::unique_ptr<DBusMethodResponse<>>, Params...),
|
|
Args... args) {
|
|
ResponseHolder response_holder;
|
|
::dbus::MethodCall method_call("test.interface", "TestMethod");
|
|
method_call.SetSerial(123);
|
|
std::unique_ptr<DBusMethodResponse<>> method_response{
|
|
new DBusMethodResponse<>(&method_call,
|
|
base::Bind(&ResponseHolder::ReceiveResponse,
|
|
response_holder.AsWeakPtr()))
|
|
};
|
|
(instance->*method)(std::move(method_response), args...);
|
|
CHECK(response_holder.response_.get())
|
|
<< "No response received. Asynchronous methods are not supported.";
|
|
ExtractMethodCallResults(response_holder.response_.get(), error);
|
|
}
|
|
};
|
|
|
|
} // namespace testing
|
|
} // namespace dbus_utils
|
|
} // namespace brillo
|
|
|
|
#endif // LIBBRILLO_BRILLO_DBUS_DBUS_OBJECT_TEST_HELPERS_H_
|