blob: e7f3cbb4ef339497db263507c5ca08d993f90743 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/dbus/utils/export_method.h"
#include <memory>
#include <string>
#include <tuple>
#include <utility>
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/ref_counted.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "components/dbus/utils/read_message.h"
#include "dbus/bus.h"
#include "dbus/message.h"
#include "dbus/mock_bus.h"
#include "dbus/mock_exported_object.h"
#include "dbus/object_path.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;
namespace dbus_utils {
namespace {
const char kInterface[] = "org.chromium.TestInterface";
const char kMethod[] = "TestMethod";
class ExportMethodTest : public testing::Test {
public:
ExportMethodTest() = default;
~ExportMethodTest() override = default;
void SetUp() override {
bus_ = base::MakeRefCounted<dbus::MockBus>(dbus::Bus::Options());
exported_object_ = base::MakeRefCounted<dbus::MockExportedObject>(
bus_.get(), dbus::ObjectPath("/org/chromium/TestObject"));
}
protected:
base::test::SingleThreadTaskEnvironment task_environment_;
scoped_refptr<dbus::MockBus> bus_;
scoped_refptr<dbus::MockExportedObject> exported_object_;
};
ExportMethodResult<bool> TestMethodCallback(std::string s, int32_t i) {
EXPECT_EQ(s, "hello");
EXPECT_EQ(i, 42);
return std::make_tuple(true);
}
} // namespace
TEST_F(ExportMethodTest, Basic) {
dbus::ExportedObject::MethodCallCallback method_call_callback;
EXPECT_CALL(*exported_object_, ExportMethod(kInterface, kMethod, _, _))
.WillOnce(
[&](Unused, Unused, dbus::ExportedObject::MethodCallCallback callback,
dbus::ExportedObject::OnExportedCallback on_exported_callback) {
method_call_callback = std::move(callback);
std::move(on_exported_callback).Run(kInterface, kMethod, true);
});
ExportMethod<"si", "b">(exported_object_.get(), kInterface, kMethod,
base::BindRepeating(&TestMethodCallback),
base::DoNothing());
ASSERT_TRUE(method_call_callback);
dbus::MethodCall method_call(kInterface, kMethod);
method_call.SetSerial(123);
dbus::MessageWriter writer(&method_call);
writer.AppendString("hello");
writer.AppendInt32(42);
bool response_sent = false;
method_call_callback.Run(
&method_call,
base::BindLambdaForTesting([&](std::unique_ptr<dbus::Response> response) {
response_sent = true;
EXPECT_EQ(response->GetReplySerial(), 123U);
auto result = internal::ReadMessage<std::tuple<bool>>(*response);
ASSERT_TRUE(result.has_value());
EXPECT_TRUE(std::get<0>(*result));
}));
EXPECT_TRUE(response_sent);
}
TEST_F(ExportMethodTest, InvalidArgs) {
dbus::ExportedObject::MethodCallCallback method_call_callback;
EXPECT_CALL(*exported_object_, ExportMethod(kInterface, kMethod, _, _))
.WillOnce(
[&](Unused, Unused, dbus::ExportedObject::MethodCallCallback callback,
dbus::ExportedObject::OnExportedCallback on_exported_callback) {
method_call_callback = std::move(callback);
std::move(on_exported_callback).Run(kInterface, kMethod, true);
});
ExportMethod<"si", "b">(exported_object_.get(), kInterface, kMethod,
base::BindRepeating(&TestMethodCallback),
base::DoNothing());
ASSERT_TRUE(method_call_callback);
dbus::MethodCall method_call(kInterface, kMethod);
method_call.SetSerial(123);
dbus::MessageWriter writer(&method_call);
writer.AppendString("hello");
// Missing int32 argument.
bool error_sent = false;
method_call_callback.Run(
&method_call,
base::BindLambdaForTesting([&](std::unique_ptr<dbus::Response> response) {
error_sent = true;
EXPECT_EQ(response->GetMessageType(), dbus::Message::MESSAGE_ERROR);
EXPECT_EQ(response->GetReplySerial(), 123U);
EXPECT_EQ(
static_cast<dbus::ErrorResponse*>(response.get())->GetErrorName(),
DBUS_ERROR_INVALID_ARGS);
}));
EXPECT_TRUE(error_sent);
}
TEST_F(ExportMethodTest, Error) {
dbus::ExportedObject::MethodCallCallback method_call_callback;
EXPECT_CALL(*exported_object_, ExportMethod(kInterface, kMethod, _, _))
.WillOnce(
[&](Unused, Unused, dbus::ExportedObject::MethodCallCallback callback,
dbus::ExportedObject::OnExportedCallback on_exported_callback) {
method_call_callback = std::move(callback);
std::move(on_exported_callback).Run(kInterface, kMethod, true);
});
ExportMethod<"si", "b">(
exported_object_.get(), kInterface, kMethod,
base::BindRepeating(
[](std::string s, int32_t i) -> ExportMethodResult<bool> {
return base::unexpected(
ExportMethodError{"org.chromium.Error", "Failed"});
}),
base::DoNothing());
ASSERT_TRUE(method_call_callback);
dbus::MethodCall method_call(kInterface, kMethod);
method_call.SetSerial(123);
dbus::MessageWriter writer(&method_call);
writer.AppendString("hello");
writer.AppendInt32(42);
bool error_sent = false;
method_call_callback.Run(
&method_call,
base::BindLambdaForTesting([&](std::unique_ptr<dbus::Response> response) {
error_sent = true;
EXPECT_EQ(response->GetMessageType(), dbus::Message::MESSAGE_ERROR);
EXPECT_EQ(response->GetReplySerial(), 123U);
auto* error_response =
static_cast<dbus::ErrorResponse*>(response.get());
EXPECT_EQ(error_response->GetErrorName(), "org.chromium.Error");
}));
EXPECT_TRUE(error_sent);
}
} // namespace dbus_utils