Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[fuchsia] Add basic implementations of fidl::AsyncEventHandler
HLCPP bindings had a `set_error_handler` method on the `InterfacePtr` but that's no longer available for `fidl::Client`. Instead, it takes a `fidl::AsyncEventHandler` that has an `on_fidl_error` method as well as methods for other events available on the `Protocol`. The new API shape creates quite a bit of extra boilerplate for just adding a closure or logging on fidl error (when a service is disconnected). This CL adds two basic implementations, one that just logs an error, and another that takes a callback. Bug: 1351487 Change-Id: If6ba87f24751af8daafb54904652164eb47d9875 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4325064 Reviewed-by: Guocheng Wei <guochengwei@chromium.org> Reviewed-by: David Dorwin <ddorwin@chromium.org> Commit-Queue: Bryant Chandler <bryantchandler@chromium.org> Cr-Commit-Position: refs/heads/main@{#1118929}
- Loading branch information
Bryant Chandler
authored and
Chromium LUCI CQ
committed
Mar 17, 2023
1 parent
5207bba
commit f412da9
Showing
7 changed files
with
254 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
// Copyright 2023 The Chromium Authors | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#ifndef BASE_FUCHSIA_FIDL_EVENT_HANDLER_H_ | ||
#define BASE_FUCHSIA_FIDL_EVENT_HANDLER_H_ | ||
|
||
#include <lib/fidl/cpp/wire/client_base.h> | ||
#include <lib/fidl/cpp/wire/status.h> | ||
|
||
#include <string> | ||
|
||
#include "base/fuchsia/fuchsia_logging.h" | ||
#include "base/functional/callback.h" | ||
#include "third_party/abseil-cpp/absl/types/optional.h" | ||
|
||
namespace fidl { | ||
template <typename Protocol> | ||
class AsyncEventHandler; | ||
} | ||
|
||
namespace base { | ||
|
||
// An implementation of `fidl::AsyncEventhandler` that simply DLOGs an error | ||
// when `on_fidl_error` is called. The lifetime of an instance of this class | ||
// needs to match the lifetime of the `fidl::Client` that it is used with. | ||
template <typename Protocol> | ||
class LoggingFidlErrorEventHandler : public fidl::AsyncEventHandler<Protocol> { | ||
public: | ||
explicit LoggingFidlErrorEventHandler( | ||
std::string protocol_name = fidl::DiscoverableProtocolName<Protocol>) | ||
: protocol_name_(std::move(protocol_name)) {} | ||
|
||
void on_fidl_error(fidl::UnbindInfo error) override { | ||
DLOG(ERROR) << protocol_name_ << " was disconnected with " | ||
<< error.status_string() << "."; | ||
} | ||
|
||
private: | ||
std::string protocol_name_; | ||
}; | ||
|
||
// An implementation of `fidl::AsyncEventHandler` that invokes the | ||
// caller-supplied callback when `on_fidl_error` is called. The lifetime of an | ||
// instance of this class needs to match the lifetime of the `fidl::Client` that | ||
// it is used with. | ||
template <typename Protocol> | ||
class CallbackFidlErrorEventHandler : public fidl::AsyncEventHandler<Protocol> { | ||
public: | ||
using OnFidlErrorCallback = base::RepeatingCallback<void(fidl::UnbindInfo)>; | ||
|
||
CallbackFidlErrorEventHandler() = delete; | ||
explicit CallbackFidlErrorEventHandler( | ||
OnFidlErrorCallback on_fidl_error_callback) | ||
: on_fidl_error_callback_(std::move(on_fidl_error_callback)) {} | ||
|
||
void on_fidl_error(fidl::UnbindInfo error) override { | ||
on_fidl_error_callback_.Run(error); | ||
} | ||
|
||
private: | ||
OnFidlErrorCallback on_fidl_error_callback_; | ||
}; | ||
|
||
} // namespace base | ||
|
||
#endif // BASE_FUCHSIA_FIDL_EVENT_HANDLER_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
// Copyright 2023 The Chromium Authors | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#include "base/fuchsia/fidl_event_handler.h" | ||
|
||
#include <fidl/base.testfidl/cpp/fidl.h> | ||
#include <fidl/fuchsia.logger/cpp/fidl.h> | ||
#include <lib/async/default.h> | ||
#include <lib/sys/cpp/component_context.h> | ||
|
||
#include "base/fuchsia/fuchsia_component_connect.h" | ||
#include "base/fuchsia/process_context.h" | ||
#include "base/fuchsia/scoped_service_binding.h" | ||
#include "base/fuchsia/test_component_context_for_process.h" | ||
#include "base/fuchsia/test_interface_natural_impl.h" | ||
#include "base/fuchsia/test_log_listener_safe.h" | ||
#include "base/task/thread_pool.h" | ||
#include "base/test/bind.h" | ||
#include "base/test/scoped_logging_settings.h" | ||
#include "base/test/task_environment.h" | ||
#include "testing/gtest/include/gtest/gtest.h" | ||
|
||
namespace { | ||
|
||
constexpr char kBaseUnittestsExec[] = "base_unittests__exec"; | ||
|
||
} // namespace | ||
|
||
namespace base { | ||
|
||
class FidlEventHandlerTest : public testing::Test { | ||
public: | ||
FidlEventHandlerTest() { | ||
test_context_.AddService( | ||
fidl::DiscoverableProtocolName<fuchsia_logger::Log>); | ||
ListenFilteredByCurrentProcessId(listener_); | ||
// Initialize logging in the `scoped_logging_settings_`. | ||
CHECK(logging::InitLogging({.logging_dest = logging::LOG_DEFAULT})); | ||
} | ||
FidlEventHandlerTest(const FidlEventHandlerTest&) = delete; | ||
FidlEventHandlerTest& operator=(const FidlEventHandlerTest&) = delete; | ||
~FidlEventHandlerTest() override = default; | ||
|
||
protected: | ||
test::SingleThreadTaskEnvironment task_environment_{ | ||
test::SingleThreadTaskEnvironment::MainThreadType::IO}; | ||
SimpleTestLogListener listener_; | ||
|
||
// Ensure that logging is directed to the system debug log. | ||
logging::ScopedLoggingSettings scoped_logging_settings_; | ||
TestComponentContextForProcess test_context_; | ||
TestInterfaceNaturalImpl test_service_; | ||
}; | ||
|
||
TEST_F(FidlEventHandlerTest, LoggingEventHandler) { | ||
LoggingFidlErrorEventHandler<base_testfidl::TestInterface> event_handler; | ||
|
||
event_handler.on_fidl_error(fidl::UnbindInfo::PeerClosed(ZX_ERR_PEER_CLOSED)); | ||
|
||
constexpr char kLogMessage[] = | ||
"base.testfidl.TestInterface was disconnected with ZX_ERR_PEER_CLOSED"; | ||
absl::optional<fuchsia_logger::LogMessage> logged_message = | ||
listener_.RunUntilMessageReceived(kLogMessage); | ||
|
||
ASSERT_TRUE(logged_message.has_value()); | ||
EXPECT_EQ(logged_message->severity(), | ||
static_cast<int32_t>(fuchsia_logger::LogLevelFilter::kError)); | ||
ASSERT_EQ(logged_message->tags().size(), 1u); | ||
EXPECT_EQ(logged_message->tags()[0], kBaseUnittestsExec); | ||
} | ||
|
||
TEST_F(FidlEventHandlerTest, LoggingEventHandler_CustomProtocolName) { | ||
LoggingFidlErrorEventHandler<base_testfidl::TestInterface> event_handler( | ||
"test_protocol_name"); | ||
|
||
event_handler.on_fidl_error(fidl::UnbindInfo::PeerClosed(ZX_ERR_PEER_CLOSED)); | ||
|
||
constexpr char kLogMessage[] = | ||
"test_protocol_name was disconnected with ZX_ERR_PEER_CLOSED"; | ||
absl::optional<fuchsia_logger::LogMessage> logged_message = | ||
listener_.RunUntilMessageReceived(kLogMessage); | ||
|
||
ASSERT_TRUE(logged_message.has_value()); | ||
EXPECT_EQ(logged_message->severity(), | ||
static_cast<int32_t>(fuchsia_logger::LogLevelFilter::kError)); | ||
ASSERT_EQ(logged_message->tags().size(), 1u); | ||
EXPECT_EQ(logged_message->tags()[0], kBaseUnittestsExec); | ||
} | ||
|
||
TEST_F(FidlEventHandlerTest, LoggingEventHandler_LogsOnServiceClosure) { | ||
LoggingFidlErrorEventHandler<base_testfidl::TestInterface> event_handler; | ||
auto client_end = fuchsia_component::ConnectAt<base_testfidl::TestInterface>( | ||
test_context_.published_services_natural()); | ||
EXPECT_TRUE(client_end.is_ok()); | ||
fidl::Client client(std::move(*client_end), async_get_default_dispatcher(), | ||
&event_handler); | ||
|
||
{ | ||
ScopedNaturalServiceBinding<base_testfidl::TestInterface> binding( | ||
ComponentContextForProcess()->outgoing().get(), &test_service_, | ||
async_get_default_dispatcher()); | ||
|
||
ASSERT_EQ(ZX_OK, VerifyTestInterface(client)); | ||
}; | ||
|
||
constexpr char kLogMessage[] = | ||
"base.testfidl.TestInterface was disconnected with ZX_ERR_PEER_CLOSED"; | ||
absl::optional<fuchsia_logger::LogMessage> logged_message = | ||
listener_.RunUntilMessageReceived(kLogMessage); | ||
|
||
ASSERT_TRUE(logged_message.has_value()); | ||
EXPECT_EQ(logged_message->severity(), | ||
static_cast<int32_t>(fuchsia_logger::LogLevelFilter::kError)); | ||
ASSERT_EQ(logged_message->tags().size(), 1u); | ||
EXPECT_EQ(logged_message->tags()[0], kBaseUnittestsExec); | ||
} | ||
|
||
TEST_F(FidlEventHandlerTest, CallbackEventHandler) { | ||
RunLoop loop; | ||
CallbackFidlErrorEventHandler<base_testfidl::TestInterface> event_handler( | ||
base::BindLambdaForTesting( | ||
[quit_closure = loop.QuitClosure()](fidl::UnbindInfo error) { | ||
ASSERT_TRUE(error.is_peer_closed()); | ||
quit_closure.Run(); | ||
})); | ||
|
||
event_handler.on_fidl_error(fidl::UnbindInfo::PeerClosed(ZX_ERR_PEER_CLOSED)); | ||
|
||
loop.Run(); | ||
} | ||
|
||
TEST_F(FidlEventHandlerTest, CallbackEventHandler_FiresOnServiceClosure) { | ||
RunLoop loop; | ||
CallbackFidlErrorEventHandler<base_testfidl::TestInterface> event_handler( | ||
base::BindLambdaForTesting( | ||
[quit_closure = loop.QuitClosure()](fidl::UnbindInfo error) { | ||
ASSERT_TRUE(error.is_peer_closed()); | ||
quit_closure.Run(); | ||
})); | ||
|
||
auto client_end = fuchsia_component::ConnectAt<base_testfidl::TestInterface>( | ||
test_context_.published_services_natural()); | ||
EXPECT_TRUE(client_end.is_ok()); | ||
fidl::Client client(std::move(*client_end), async_get_default_dispatcher(), | ||
&event_handler); | ||
|
||
{ | ||
ScopedNaturalServiceBinding<base_testfidl::TestInterface> binding( | ||
ComponentContextForProcess()->outgoing().get(), &test_service_, | ||
async_get_default_dispatcher()); | ||
|
||
ASSERT_EQ(ZX_OK, VerifyTestInterface(client)); | ||
}; | ||
|
||
loop.Run(); | ||
} | ||
|
||
} // namespace base |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters