blob: 0a07da395db1706e3561874957770b32f9a230d7 [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 "src/ui/bin/root_presenter/virtual_keyboard_manager.h"
#include <fuchsia/input/virtualkeyboard/cpp/fidl.h>
#include <lib/sys/cpp/testing/component_context_provider.h>
#include <lib/syslog/cpp/macros.h>
#include <zircon/status.h>
#include <gtest/gtest.h>
#include <src/lib/testing/loop_fixture/test_loop_fixture.h>
#include "src/lib/fxl/memory/weak_ptr.h"
#include "src/ui/bin/root_presenter/virtual_keyboard_coordinator.h"
namespace root_presenter {
namespace virtual_keyboard_manager {
namespace {
class FakeVirtualKeyboardCoordinator : public VirtualKeyboardCoordinator {
public:
FakeVirtualKeyboardCoordinator() : weak_ptr_factory_(this) {}
virtual ~FakeVirtualKeyboardCoordinator() = default;
// |VirtualKeyboardCoordinator|
void NotifyVisibilityChange(
bool is_visible, fuchsia::input::virtualkeyboard::VisibilityChangeReason reason) override {
is_visible_ = is_visible;
change_reason_ = reason;
}
void NotifyManagerError(zx_status_t error) override { manager_error_ = error; }
void RequestTypeAndVisibility(zx_koid_t requestor_view_koid,
fuchsia::input::virtualkeyboard::TextType text_type,
bool is_visibile) override {
FX_NOTIMPLEMENTED();
}
void NotifyFocusChange(fuchsia::ui::views::ViewRef focused_view) override { FX_NOTIMPLEMENTED(); }
// Test support.
const auto& is_visible() { return is_visible_; }
const auto& change_reason() { return change_reason_; }
const auto& manager_error() { return manager_error_; }
fxl::WeakPtr<FakeVirtualKeyboardCoordinator> GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
private:
std::optional<bool> is_visible_;
std::optional<fuchsia::input::virtualkeyboard::VisibilityChangeReason> change_reason_;
std::optional<zx_status_t> manager_error_;
fxl::WeakPtrFactory<FakeVirtualKeyboardCoordinator> weak_ptr_factory_;
};
class VirtualKeyboardManagerTest : public gtest::TestLoopFixture {
protected:
VirtualKeyboardManagerTest() {}
auto* context_provider() { return &context_provider_; }
fxl::WeakPtr<FakeVirtualKeyboardCoordinator> coordinator() { return coordinator_.GetWeakPtr(); }
private:
sys::testing::ComponentContextProvider context_provider_;
FakeVirtualKeyboardCoordinator coordinator_;
};
TEST_F(VirtualKeyboardManagerTest, CtorDoesNotCrash) {
VirtualKeyboardManager(coordinator(), fuchsia::input::virtualkeyboard::TextType::ALPHANUMERIC);
}
TEST_F(VirtualKeyboardManagerTest, FirstWatchReturnsImmediately) {
bool was_called = false;
VirtualKeyboardManager(coordinator(), fuchsia::input::virtualkeyboard::TextType::ALPHANUMERIC)
.WatchTypeAndVisibility([&was_called](fuchsia::input::virtualkeyboard::TextType text_type,
bool is_visible) { was_called = true; });
ASSERT_TRUE(was_called);
}
TEST_F(VirtualKeyboardManagerTest, InitialVisibilityIsFalse) {
std::optional<bool> is_visible;
VirtualKeyboardManager(coordinator(), fuchsia::input::virtualkeyboard::TextType::ALPHANUMERIC)
.WatchTypeAndVisibility([&](fuchsia::input::virtualkeyboard::TextType text_type,
bool is_vis) { is_visible = is_vis; });
ASSERT_EQ(false, is_visible);
}
TEST_F(VirtualKeyboardManagerTest, SecondWatchHangsUntilChange) {
VirtualKeyboardManager manager(coordinator(),
fuchsia::input::virtualkeyboard::TextType::ALPHANUMERIC);
// Make the initial call to WatchTypeAndVisibility(), which invokes its callback immediately, so
// that the next call will block until type or visibility changes.
manager.WatchTypeAndVisibility(
[](fuchsia::input::virtualkeyboard::TextType text_type, bool is_visible) {});
// Invoke WatchTypeAndVisibility() again without changing either parameter. This call should
// _not_ invoke its callback yet.
bool was_called = false;
manager.WatchTypeAndVisibility([&](fuchsia::input::virtualkeyboard::TextType text_type,
bool is_visible) { was_called = true; });
ASSERT_FALSE(was_called);
// Make a no-op request. WatchTypeAndVisibility() should _not_ invoke its callback yet.
manager.SetTypeAndVisibility(fuchsia::input::virtualkeyboard::TextType::ALPHANUMERIC, false);
ASSERT_FALSE(was_called);
// Change visibility. Now, Manager should invoke its callback.
manager.SetTypeAndVisibility(fuchsia::input::virtualkeyboard::TextType::PHONE, false);
ASSERT_TRUE(was_called);
}
TEST_F(VirtualKeyboardManagerTest, SecondWatchReturnsImmediatelyIfAlreadyChanged) {
VirtualKeyboardManager manager(coordinator(),
fuchsia::input::virtualkeyboard::TextType::ALPHANUMERIC);
// Make the initial call to WatchTypeAndVisibility(), which invokes its callback immediately, so
// that we know we're exercising the second-and-later logic below.
manager.WatchTypeAndVisibility(
[](fuchsia::input::virtualkeyboard::TextType text_type, bool is_visibile) {});
// Make a change before invoking WatchTypeAndVisibility() again.
manager.SetTypeAndVisibility(fuchsia::input::virtualkeyboard::TextType::PHONE, true);
// Invoke WatchTypeAndVisibility() again. The callback should be invoked immediately.
bool was_called = false;
manager.WatchTypeAndVisibility([&](fuchsia::input::virtualkeyboard::TextType text_type,
bool is_visibile) { was_called = true; });
ASSERT_TRUE(was_called);
}
TEST_F(VirtualKeyboardManagerTest, FirstWatchCallbackIsOnlyInvokedOnce) {
// Make the initial call to WatchTypeAndVisibility(), which invokes its callback immediately.
size_t n_calls = 0;
VirtualKeyboardManager manager(coordinator(),
fuchsia::input::virtualkeyboard::TextType::ALPHANUMERIC);
manager.WatchTypeAndVisibility([&n_calls](fuchsia::input::virtualkeyboard::TextType text_type,
bool is_visible) { ++n_calls; });
ASSERT_EQ(1u, n_calls);
manager.SetTypeAndVisibility(fuchsia::input::virtualkeyboard::TextType::PHONE, false);
ASSERT_EQ(1u, n_calls);
}
TEST_F(VirtualKeyboardManagerTest, SecondWatchCallbackIsOnlyInvokedOnce) {
// Make the initial call to WatchTypeAndVisibility(), which invokes its callback immediately,
// so that we know we're exercising the second-and-later logic below.
VirtualKeyboardManager manager(coordinator(),
fuchsia::input::virtualkeyboard::TextType::ALPHANUMERIC);
manager.WatchTypeAndVisibility(
[](fuchsia::input::virtualkeyboard::TextType text_type, bool is_visible) {});
// Set a watch, and make a change, causing the watch to fire.
size_t n_callbacks = 0;
manager.WatchTypeAndVisibility(
[&](fuchsia::input::virtualkeyboard::TextType text_type, bool visibility) { ++n_callbacks; });
manager.SetTypeAndVisibility(fuchsia::input::virtualkeyboard::TextType::PHONE, false);
ASSERT_EQ(1u, n_callbacks);
// Watches are one-shot, so another should _not_ trigger another callback.
manager.SetTypeAndVisibility(fuchsia::input::virtualkeyboard::TextType::PHONE, true);
ASSERT_EQ(1u, n_callbacks);
}
TEST_F(VirtualKeyboardManagerTest, ConcurrentWatchesReportErrorToCoordinator) {
// Make the initial call to WatchTypeAndVisibility(), which invokes its callback immediately,
// so that we know we're exercising the second-and-later logic below.
VirtualKeyboardManager manager(coordinator(),
fuchsia::input::virtualkeyboard::TextType::ALPHANUMERIC);
manager.WatchTypeAndVisibility(
[](fuchsia::input::virtualkeyboard::TextType text_type, bool is_visible) {});
// Set up first watch.
bool was_first_callback_called = false;
manager.WatchTypeAndVisibility([&](fuchsia::input::virtualkeyboard::TextType text_type,
bool visibility) { was_first_callback_called = true; });
// Set up second watch.
bool was_second_callback_called = false;
manager.WatchTypeAndVisibility([&](fuchsia::input::virtualkeyboard::TextType text_type,
bool visibility) { was_second_callback_called = true; });
ASSERT_EQ(ZX_ERR_BAD_STATE, coordinator()->manager_error());
}
class VirtualKeyboardManagerWatchTestFixture
: public VirtualKeyboardManagerTest,
public testing::WithParamInterface<
std::tuple<fuchsia::input::virtualkeyboard::TextType, bool>> {};
TEST_P(VirtualKeyboardManagerWatchTestFixture, WatchProvidesCorrectValues) {
const auto& [expected_text_type, expected_visibility] = GetParam();
std::optional<fuchsia::input::virtualkeyboard::TextType> actual_text_type;
std::optional<bool> actual_visibility;
VirtualKeyboardManager manager(coordinator(),
fuchsia::input::virtualkeyboard::TextType::ALPHANUMERIC);
manager.SetTypeAndVisibility(expected_text_type, expected_visibility);
manager.WatchTypeAndVisibility(
[&](fuchsia::input::virtualkeyboard::TextType text_type, bool is_visible) {
actual_text_type = text_type;
actual_visibility = is_visible;
});
EXPECT_EQ(expected_text_type, actual_text_type);
EXPECT_EQ(expected_visibility, actual_visibility);
}
INSTANTIATE_TEST_SUITE_P(
VirtualKeyboardManagerWatchTests, VirtualKeyboardManagerWatchTestFixture,
::testing::Combine(::testing::Values(fuchsia::input::virtualkeyboard::TextType::ALPHANUMERIC,
fuchsia::input::virtualkeyboard::TextType::NUMERIC,
fuchsia::input::virtualkeyboard::TextType::PHONE),
::testing::Values(false, true)));
TEST_F(VirtualKeyboardManagerTest, NotifyInvokesCallback) {
bool was_called = false;
VirtualKeyboardManager(coordinator(), fuchsia::input::virtualkeyboard::TextType::ALPHANUMERIC)
.Notify(true, fuchsia::input::virtualkeyboard::VisibilityChangeReason::USER_INTERACTION,
[&was_called]() { was_called = true; });
ASSERT_TRUE(was_called);
}
TEST_F(VirtualKeyboardManagerTest, NotifyInvokesCallbackEvenIfCoordinatorIsNull) {
bool was_called = false;
std::optional<FakeVirtualKeyboardCoordinator> coordinator(std::in_place);
VirtualKeyboardManager manager(coordinator->GetWeakPtr(),
fuchsia::input::virtualkeyboard::TextType::ALPHANUMERIC);
coordinator.reset();
manager.Notify(true, fuchsia::input::virtualkeyboard::VisibilityChangeReason::USER_INTERACTION,
[&was_called]() { was_called = true; });
ASSERT_TRUE(was_called);
}
class VirtualKeyboardManagerNotifyTestFixture
: public VirtualKeyboardManagerTest,
public testing::WithParamInterface<
std::tuple<bool, fuchsia::input::virtualkeyboard::VisibilityChangeReason>> {};
TEST_P(VirtualKeyboardManagerNotifyTestFixture, InformsCoordinator) {
const auto& [expected_visibility, expected_reason] = GetParam();
VirtualKeyboardManager(coordinator(), fuchsia::input::virtualkeyboard::TextType::ALPHANUMERIC)
.Notify(expected_visibility, expected_reason, []() {});
EXPECT_EQ(expected_visibility, coordinator()->is_visible());
EXPECT_EQ(expected_reason, coordinator()->change_reason());
}
INSTANTIATE_TEST_SUITE_P(
VirtualKeyboardManagerNotifyTests, VirtualKeyboardManagerNotifyTestFixture,
::testing::Combine(
::testing::Values(false, true),
::testing::Values(fuchsia::input::virtualkeyboard::VisibilityChangeReason::USER_INTERACTION,
fuchsia::input::virtualkeyboard::VisibilityChangeReason::PROGRAMMATIC)));
TEST_F(VirtualKeyboardManagerTest, SetVisibilityUpdatesVisibilityAndPreservesInitialTextType) {
VirtualKeyboardManager manager(
coordinator(), fuchsia::input::virtualkeyboard::TextType::NUMERIC /* non-zero value */);
std::optional<fuchsia::input::virtualkeyboard::TextType> text_type;
std::optional<bool> is_visible;
// With no previous call to SetTypeAndVisility() or WatchTypeAndVisibility(), the call below
// should receive the initial TextType passed to the manager ctor.
manager.SetVisibility(true);
manager.WatchTypeAndVisibility([&](fuchsia::input::virtualkeyboard::TextType ttype, bool is_vis) {
text_type = ttype;
is_visible = is_vis;
});
EXPECT_EQ(fuchsia::input::virtualkeyboard::TextType::NUMERIC, text_type);
EXPECT_EQ(true, is_visible);
}
TEST_F(VirtualKeyboardManagerTest, SetVisibilityUpdatesVisibilityAndPreservesPendingTextType) {
VirtualKeyboardManager manager(coordinator(),
fuchsia::input::virtualkeyboard::TextType::ALPHANUMERIC);
std::optional<fuchsia::input::virtualkeyboard::TextType> text_type;
std::optional<bool> is_visible;
manager.SetTypeAndVisibility(fuchsia::input::virtualkeyboard::TextType::NUMERIC, true);
// With a previous call to SetTypeAndVisility(), but no previous call to WatchTypeAndVisibility(),
// the call below should receive the TextType passed to SetTypeAndVisibiilty() above.
manager.SetVisibility(false);
manager.WatchTypeAndVisibility([&](fuchsia::input::virtualkeyboard::TextType ttype, bool is_vis) {
text_type = ttype;
is_visible = is_vis;
});
EXPECT_EQ(fuchsia::input::virtualkeyboard::TextType::NUMERIC, text_type);
EXPECT_EQ(false, is_visible);
}
TEST_F(VirtualKeyboardManagerTest, SetVisibilityUpdatesVisibilityAndPreservesLastSentTextType) {
VirtualKeyboardManager manager(coordinator(),
fuchsia::input::virtualkeyboard::TextType::ALPHANUMERIC);
std::optional<fuchsia::input::virtualkeyboard::TextType> text_type;
std::optional<bool> is_visible;
manager.SetTypeAndVisibility(fuchsia::input::virtualkeyboard::TextType::NUMERIC, true);
manager.WatchTypeAndVisibility(
[](fuchsia::input::virtualkeyboard::TextType ttype, bool is_vis) {});
// With a previous call to SetTypeAndVisility(), and a previous call to WatchTypeAndVisibility(),
// the call below should receive the same value as the previous call to WatchTypeAndVisibiility().
manager.SetVisibility(false);
manager.WatchTypeAndVisibility([&](fuchsia::input::virtualkeyboard::TextType ttype, bool is_vis) {
text_type = ttype;
is_visible = is_vis;
});
EXPECT_EQ(fuchsia::input::virtualkeyboard::TextType::NUMERIC, text_type);
EXPECT_EQ(false, is_visible);
}
} // namespace
} // namespace virtual_keyboard_manager
} // namespace root_presenter