| // Copyright 2021 The Fuchsia 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 "src/ui/bin/root_presenter/focus_dispatcher.h" |
| |
| #include <fuchsia/ui/focus/cpp/fidl.h> |
| #include <fuchsia/ui/input/cpp/fidl.h> |
| #include <lib/async/dispatcher.h> |
| #include <lib/gtest/real_loop_fixture.h> |
| #include <lib/sys/cpp/testing/component_context_provider.h> |
| #include <lib/ui/scenic/cpp/commands.h> |
| #include <lib/ui/scenic/cpp/view_ref_pair.h> |
| #include <lib/ui/scenic/cpp/view_token_pair.h> |
| #include <zircon/status.h> |
| |
| #include <vector> |
| |
| #include <gtest/gtest.h> |
| #include <src/lib/fxl/macros.h> |
| #include <src/lib/testing/loop_fixture/test_loop_fixture.h> |
| |
| #include "src/ui/bin/root_presenter/tests/fakes/fake_keyboard_focus_controller.h" |
| |
| namespace root_presenter { |
| |
| using fuchsia::ui::focus::FocusChain; |
| using fuchsia::ui::focus::FocusChainListener; |
| using fuchsia::ui::focus::FocusChainListenerRegistry; |
| using fuchsia::ui::keyboard::focus::Controller; |
| using fuchsia::ui::views::ViewRef; |
| |
| class FocusDispatcherTest : public gtest::RealLoopFixture, public FocusChainListenerRegistry { |
| public: |
| void SetUp() final { |
| // Installs 'this' as a fake server for FocusChainListenerRegistry. |
| context_provider_.service_directory_provider()->AddService( |
| focus_listener_registry_.GetHandler(this)); |
| |
| // Installs a fake receiver for keyboard focus events, and asks it to flip |
| // a flag if a notification comes in. |
| fake_keyboard_focus_controller_ = |
| std::make_unique<testing::FakeKeyboardFocusController>(context_provider_); |
| fake_keyboard_focus_controller_->SetOnNotify( |
| [&](const ViewRef& view_ref) { keyboard_notification_received_ = true; }); |
| |
| controller_handler_ = fake_keyboard_focus_controller_->GetHandler(); |
| |
| // Finally, initializes the unit under test. |
| focus_dispatch_ = std::make_unique<FocusDispatcher>(context_provider_.context()->svc()); |
| } |
| |
| // Implements `fuchsia.ui.focus.FocusChainListenerRegistry`, but only for a single |
| // listener registration. |
| void Register(fidl::InterfaceHandle<FocusChainListener> listener) override { |
| ASSERT_EQ(ZX_OK, focus_chain_listener_.Bind(std::move(listener))); |
| focus_chain_listener_.set_error_handler([](zx_status_t status) { |
| FAIL() << "error while talking to focus chain listener: " << zx_status_get_string(status); |
| }); |
| register_calls_++; |
| } |
| |
| void SendFocusChain(FocusChain focus_chain) { |
| focus_chain_listener_->OnFocusChange(std::move(focus_chain), [&] { focus_dispatched_++; }); |
| } |
| |
| void ChangeFocus(std::vector<ViewRef> view_refs) { |
| FocusChain focus_chain; |
| focus_chain.set_focus_chain(std::move(view_refs)); |
| SendFocusChain(std::move(focus_chain)); |
| } |
| |
| void SendEmptyFocus() { |
| FocusChain focus_chain; |
| SendFocusChain(std::move(focus_chain)); |
| } |
| |
| ViewRef MakeViewRef() { |
| auto view_ref_pair = scenic::ViewRefPair::New(); |
| return fidl::Clone(view_ref_pair.view_ref); |
| } |
| |
| protected: |
| sys::testing::ComponentContextProvider context_provider_; |
| |
| fidl::BindingSet<fuchsia::ui::focus::FocusChainListenerRegistry> focus_listener_registry_; |
| |
| std::unique_ptr<testing::FakeKeyboardFocusController> fake_keyboard_focus_controller_; |
| |
| // The client-end connection to a test FocusChainListener. |
| fidl::InterfacePtr<FocusChainListener> focus_chain_listener_; |
| |
| fidl::InterfaceRequestHandler<Controller> controller_handler_; |
| |
| // Class under test. |
| std::unique_ptr<FocusDispatcher> focus_dispatch_; |
| |
| bool keyboard_notification_received_{}; |
| int focus_dispatched_{}; |
| int register_calls_{}; |
| }; |
| |
| TEST_F(FocusDispatcherTest, Forward) { |
| // Give the opportunity for Register(...) to get called. |
| RunLoopUntilIdle(); |
| ASSERT_NE(0, register_calls_) << "FocusDispatcher should call Register"; |
| |
| std::vector<ViewRef> v; |
| v.emplace_back(MakeViewRef()); |
| ChangeFocus(std::move(v)); |
| |
| RunLoopUntilIdle(); |
| EXPECT_NE(0, focus_dispatched_) << "ChangeFocus should have dispatched OnFocusChange"; |
| EXPECT_TRUE(keyboard_notification_received_); |
| } |
| |
| TEST_F(FocusDispatcherTest, EmptyFocusChain) { |
| RunLoopUntilIdle(); |
| ASSERT_NE(0, register_calls_) << "FocusDispatcher should call Register"; |
| |
| ChangeFocus({}); |
| |
| RunLoopUntilIdle(); |
| EXPECT_NE(0, focus_dispatched_) << "ChangeFocus should have dispatched OnFocusChange"; |
| |
| // Nothing is called with an empty focus chain. |
| EXPECT_FALSE(keyboard_notification_received_); |
| } |
| |
| TEST_F(FocusDispatcherTest, UnsetFocusChain) { |
| RunLoopUntilIdle(); |
| ASSERT_NE(0, register_calls_) << "FocusDispatcher should call Register"; |
| |
| SendEmptyFocus(); |
| |
| RunLoopUntilIdle(); |
| EXPECT_NE(0, focus_dispatched_) << "ChangeFocus should have dispatched OnFocusChange"; |
| |
| // Nothing is called with an empty focus chain. |
| EXPECT_FALSE(keyboard_notification_received_); |
| } |
| |
| } // namespace root_presenter |