blob: 410246d6ffc1b2de386693960d6a6e1438d30bc1 [file] [log] [blame]
// 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 <fuchsia/ui/views/cpp/fidl.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/zx/time.h>
#include <optional>
#include <gtest/gtest.h>
#include "src/lib/testing/loop_fixture/test_loop_fixture.h"
#include "src/ui/scenic/lib/focus/focus_manager.h"
#include "src/ui/scenic/lib/view_tree/snapshot_types.h"
// This test exercises the implementation of the fuchsia.ui.views.ViewRefFocused protocol, which
// allows clients to listen to view-focus events.
//
// Visual geometry is not important in this test. We use the following two-node tree topology:
// A
// |
// B
namespace {
enum : zx_koid_t { kNodeA = 1, kNodeB };
// Creates a snapshot with the following two-node topology:
// A
// |
// B
std::shared_ptr<const view_tree::Snapshot> TwoNodeSnapshot() {
auto snapshot = std::make_shared<view_tree::Snapshot>();
snapshot->root = kNodeA;
auto& view_tree = snapshot->view_tree;
view_tree[kNodeA] = view_tree::ViewNode{.parent = ZX_KOID_INVALID, .children = {kNodeB}};
view_tree[kNodeB] = view_tree::ViewNode{.parent = kNodeA};
return snapshot;
}
// Class fixture for TEST_F.
class ViewRefFocusedTest : public gtest::TestLoopFixture {
protected:
ViewRefFocusedTest() {
focus_manager_.RegisterViewRefFocused(kNodeA, node_a_focused_.NewRequest());
focus_manager_.RegisterViewRefFocused(kNodeB, node_b_focused_.NewRequest());
FX_CHECK(node_a_focused_.is_bound());
FX_CHECK(node_b_focused_.is_bound());
}
focus::FocusManager focus_manager_;
fuchsia::ui::views::ViewRefFocusedPtr node_a_focused_;
fuchsia::ui::views::ViewRefFocusedPtr node_b_focused_;
};
TEST_F(ViewRefFocusedTest, NoFocus_NoResponse) {
// No snapshots declared yet, "empty scene".
bool called_node_a = false;
node_a_focused_->Watch([&called_node_a](auto) { called_node_a = true; });
bool called_node_b = false;
node_b_focused_->Watch([&called_node_b](auto) { called_node_b = true; });
RunLoopUntilIdle();
EXPECT_FALSE(called_node_a);
EXPECT_FALSE(called_node_b);
}
TEST_F(ViewRefFocusedTest, BasicTree_ParentGetsFocus) {
focus_manager_.OnNewViewTreeSnapshot(TwoNodeSnapshot());
std::optional<bool> node_a_focus;
node_a_focused_->Watch([&node_a_focus](auto update) {
ASSERT_TRUE(update.has_focused());
node_a_focus = std::optional<bool>(update.focused());
});
bool called_node_b = false;
node_b_focused_->Watch([&called_node_b](auto) { called_node_b = true; });
RunLoopUntilIdle();
ASSERT_TRUE(node_a_focus.has_value()); // received a focus event
EXPECT_TRUE(node_a_focus.value()); // node A has focus
EXPECT_FALSE(called_node_b);
}
TEST_F(ViewRefFocusedTest, ChildFocus_FalseToTrue) {
focus_manager_.OnNewViewTreeSnapshot(TwoNodeSnapshot());
// Poll after node B gains focus.
std::optional<bool> node_b_focus;
node_b_focused_->Watch([&node_b_focus](auto update) {
ASSERT_TRUE(update.has_focused());
node_b_focus = std::optional<bool>(update.focused());
});
RunLoopUntilIdle();
EXPECT_FALSE(node_b_focus.has_value());
focus_manager_.RequestFocus(kNodeA, kNodeB);
RunLoopUntilIdle();
ASSERT_TRUE(node_b_focus.has_value());
EXPECT_TRUE(node_b_focus.value());
}
TEST_F(ViewRefFocusedTest, ChildFocus_FalseToFalse) {
focus_manager_.OnNewViewTreeSnapshot(TwoNodeSnapshot());
focus_manager_.RequestFocus(kNodeA, kNodeB);
focus_manager_.RequestFocus(kNodeA, kNodeA);
// Poll after node B gains then loses focus.
std::optional<bool> node_b_focus;
node_b_focused_->Watch([&node_b_focus](auto update) {
ASSERT_TRUE(update.has_focused());
node_b_focus = std::optional<bool>(update.focused());
});
RunLoopUntilIdle();
ASSERT_TRUE(node_b_focus.has_value());
EXPECT_FALSE(node_b_focus.value());
}
TEST_F(ViewRefFocusedTest, ChildFocus_TrueToFalse) {
focus_manager_.OnNewViewTreeSnapshot(TwoNodeSnapshot());
focus_manager_.RequestFocus(kNodeA, kNodeB);
// First poll by node B sees focus gained.
std::optional<bool> node_b_focus;
node_b_focused_->Watch([&node_b_focus](auto update) {
ASSERT_TRUE(update.has_focused());
node_b_focus = std::optional<bool>(update.focused());
});
RunLoopUntilIdle();
ASSERT_TRUE(node_b_focus.has_value());
EXPECT_TRUE(node_b_focus.value());
// Second poll by node B sees focus lost.
node_b_focus.reset();
node_b_focused_->Watch([&node_b_focus](auto update) {
ASSERT_TRUE(update.has_focused());
node_b_focus = std::optional<bool>(update.focused());
});
focus_manager_.RequestFocus(kNodeA, kNodeA);
RunLoopUntilIdle();
ASSERT_TRUE(node_b_focus.has_value());
EXPECT_FALSE(node_b_focus.value());
}
TEST_F(ViewRefFocusedTest, ChildFocus_TrueToTrue) {
focus_manager_.OnNewViewTreeSnapshot(TwoNodeSnapshot());
focus_manager_.RequestFocus(kNodeA, kNodeB);
// First poll by node B sees focus gained.
std::optional<bool> node_b_focus;
node_b_focused_->Watch([&node_b_focus](auto update) {
ASSERT_TRUE(update.has_focused());
node_b_focus = std::optional<bool>(update.focused());
});
RunLoopUntilIdle();
ASSERT_TRUE(node_b_focus.has_value());
EXPECT_TRUE(node_b_focus.value());
// Second poll by node B sees focus lost then gained.
node_b_focus.reset();
node_b_focused_->Watch([&node_b_focus](auto update) {
ASSERT_TRUE(update.has_focused());
node_b_focus = std::optional<bool>(update.focused());
});
focus_manager_.RequestFocus(kNodeA, kNodeA);
focus_manager_.RequestFocus(kNodeA, kNodeB);
RunLoopUntilIdle();
ASSERT_TRUE(node_b_focus.has_value());
EXPECT_TRUE(node_b_focus.value());
}
} // namespace