#include "ui/ozone/platform/wayland/host/wayland_input_method_context.h"
#include <text-input-unstable-v1-server-protocol.h>
#include <wayland-server.h>
#include <memory>
#include <optional>
#include <string_view>
#include "base/environment.h"
#include "base/i18n/break_iterator.h"
#include "base/memory/raw_ptr.h"
#include "base/nix/xdg_util.h"
#include "base/strings/utf_string_conversions.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/ime/ime_text_span.h"
#include "ui/base/ime/linux/linux_input_method_context.h"
#include "ui/base/ime/text_input_client.h"
#include "ui/base/ime/text_input_flags.h"
#include "ui/base/ime/text_input_type.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/event.h"
#include "ui/events/ozone/events_ozone.h"
#include "ui/gfx/range/range.h"
#include "ui/ozone/platform/wayland/host/wayland_event_source.h"
#include "ui/ozone/platform/wayland/host/wayland_seat.h"
#include "ui/ozone/platform/wayland/host/wayland_window.h"
#include "ui/ozone/platform/wayland/test/mock_surface.h"
#include "ui/ozone/platform/wayland/test/mock_zcr_extended_text_input.h"
#include "ui/ozone/platform/wayland/test/mock_zwp_text_input.h"
#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
#include "ui/ozone/platform/wayland/test/test_zcr_text_input_extension.h"
#include "ui/ozone/platform/wayland/test/wayland_test.h"
_;
DoAll;
InSequence;
Mock;
Optional;
SaveArg;
Values;
namespace ui {
std::optional<size_t> CountGraphemeCluster(std::u16string_view text) { … }
class MockTextInputClient : public TextInputClient { … };
class MockZWPTextInputWrapper : public ZWPTextInputWrapper { … };
class TestInputMethodContextDelegate : public LinuxInputMethodContextDelegate { … };
class TestKeyboardDelegate : public WaylandKeyboard::Delegate { … };
class WaylandInputMethodContextTestBase : public WaylandTest { … };
WaylandInputMethodContextTest;
WaylandInputMethodContextOldServerTest;
INSTANTIATE_TEST_SUITE_P(…);
INSTANTIATE_TEST_SUITE_P(…);
TEST_P(WaylandInputMethodContextOldServerTest, SetInputType) { … }
TEST_P(WaylandInputMethodContextTest, ActivateDeactivate) { … }
TEST_P(WaylandInputMethodContextTest, Reset) { … }
TEST_P(WaylandInputMethodContextTest, SetCursorLocation) { … }
TEST_P(WaylandInputMethodContextTest, SetSurroundingTextForShortText) { … }
TEST_P(WaylandInputMethodContextTest, SetSurroundingTextForLongText) { … }
TEST_P(WaylandInputMethodContextTest, SetSurroundingTextForLongTextInLeftEdge) { … }
TEST_P(WaylandInputMethodContextTest,
SetSurroundingTextForLongTextInRightEdge) { … }
TEST_P(WaylandInputMethodContextTest, DISABLED_SetSurroundingTextForLongRange) { … }
TEST_P(WaylandInputMethodContextTest,
SetSurroundingTextForShortTextWithGrammmarFragment) { … }
TEST_P(WaylandInputMethodContextTest,
SetSurroundingTextForLongTextWithGrammmarFragment) { … }
TEST_P(WaylandInputMethodContextTest,
SetSurroundingTextForShortTextWithAutocorrect) { … }
TEST_P(WaylandInputMethodContextTest, DeleteSurroundingTextWithExtendedRange) { … }
TEST_P(WaylandInputMethodContextTest, DeleteSurroundingTextInIncorrectOrder) { … }
TEST_P(WaylandInputMethodContextTest,
DeleteSurroundingTextAndCommitInIncorrectOrder) { … }
TEST_P(WaylandInputMethodContextTest, SetInputType) { … }
TEST_P(WaylandInputMethodContextTest, SetInputTypeWithoutLearning) { … }
TEST_P(WaylandInputMethodContextTest,
SetInputTypeWithoutInlineCompositionSupport) { … }
TEST_P(WaylandInputMethodContextTest, SetInputTypeAfterFocus) { … }
TEST_P(WaylandInputMethodContextTest, OnPreeditChanged) { … }
TEST_P(WaylandInputMethodContextTest, OnCommit) { … }
TEST_P(WaylandInputMethodContextTest,
OnCommitAfterEmptyPreeditStringWithoutCursor) { … }
TEST_P(WaylandInputMethodContextTest, OnCommitAfterPreeditStringWithoutCursor) { … }
#if BUILDFLAG(IS_CHROMEOS_LACROS)
TEST_P(WaylandInputMethodContextTest, OnConfirmCompositionText) {
constexpr char16_t text[] = u"ab😀cあdef";
constexpr gfx::Range range(5, 6);
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->text_input_manager_v1()->text_input(),
SetSurroundingText("ab😀cあdef", gfx::Range(7, 10)));
});
input_method_context_->SetSurroundingText(text, gfx::Range(0, 9),
gfx::Range::InvalidRange(), range,
std::nullopt, std::nullopt);
EXPECT_EQ(
input_method_context_->predicted_state_for_testing().surrounding_text,
text);
EXPECT_EQ(input_method_context_->predicted_state_for_testing().selection,
range);
connection_->Flush();
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
auto* text_input = server->text_input_manager_v1()->text_input();
Mock::VerifyAndClearExpectations(text_input);
const gfx::Range sent_range(10, 7);
zwp_text_input_v1_send_cursor_position(
text_input->resource(), sent_range.start(), sent_range.end());
zwp_text_input_v1_send_commit_string(text_input->resource(), 0,
"ab😀cあdef");
});
EXPECT_THAT(input_method_context_delegate_->last_on_confirm_composition_arg(),
Optional(true));
EXPECT_EQ(
input_method_context_->predicted_state_for_testing().surrounding_text,
text);
EXPECT_EQ(input_method_context_->predicted_state_for_testing().selection,
range);
EXPECT_EQ(input_method_context_->predicted_state_for_testing().composition,
gfx::Range(0));
}
TEST_P(WaylandInputMethodContextTest,
OnConfirmCompositionTextExtendedKeepSelectionNoComposition) {
input_method_context_->SetSurroundingText(
u"abcd", gfx::Range(0, 4), gfx::Range::InvalidRange(), gfx::Range(0, 4),
std::nullopt, std::nullopt);
connection_->Flush();
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
zcr_extended_text_input_v1_send_confirm_preedit(
server->text_input_extension_v1()->extended_text_input()->resource(),
ZCR_EXTENDED_TEXT_INPUT_V1_CONFIRM_PREEDIT_SELECTION_BEHAVIOR_UNCHANGED);
});
EXPECT_THAT(input_method_context_delegate_->last_on_confirm_composition_arg(),
Optional(true));
EXPECT_EQ(input_method_context_->predicted_state_for_testing().selection,
gfx::Range(0, 4));
}
TEST_P(WaylandInputMethodContextTest,
OnConfirmCompositionTextExtendedKeepSelectionComposition) {
input_method_context_->SetSurroundingText(
u"abcd", gfx::Range(0, 4), gfx::Range::InvalidRange(), gfx::Range(2),
std::nullopt, std::nullopt);
input_method_context_->OnPreeditString("xyz", {}, gfx::Range(1));
connection_->Flush();
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
zcr_extended_text_input_v1_send_confirm_preedit(
server->text_input_extension_v1()->extended_text_input()->resource(),
ZCR_EXTENDED_TEXT_INPUT_V1_CONFIRM_PREEDIT_SELECTION_BEHAVIOR_UNCHANGED);
});
EXPECT_THAT(input_method_context_delegate_->last_on_confirm_composition_arg(),
Optional(true));
EXPECT_EQ(input_method_context_->predicted_state_for_testing().selection,
gfx::Range(3));
}
TEST_P(WaylandInputMethodContextTest,
OnConfirmCompositionTextExtendedDontKeepSelectionNoComposition) {
input_method_context_->SetSurroundingText(
u"abcd", gfx::Range(0, 4), gfx::Range::InvalidRange(), gfx::Range(0, 4),
std::nullopt, std::nullopt);
connection_->Flush();
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
zcr_extended_text_input_v1_send_confirm_preedit(
server->text_input_extension_v1()->extended_text_input()->resource(),
ZCR_EXTENDED_TEXT_INPUT_V1_CONFIRM_PREEDIT_SELECTION_BEHAVIOR_AFTER_PREEDIT);
});
EXPECT_THAT(input_method_context_delegate_->last_on_confirm_composition_arg(),
Optional(false));
EXPECT_EQ(input_method_context_->predicted_state_for_testing().selection,
gfx::Range(0, 4));
EXPECT_EQ(input_method_context_->predicted_state_for_testing().composition,
gfx::Range(0));
}
TEST_P(WaylandInputMethodContextTest,
OnConfirmCompositionTextExtendedDontKeepSelectionComposition) {
input_method_context_->SetSurroundingText(
u"abcd", gfx::Range(0, 4), gfx::Range::InvalidRange(), gfx::Range(2),
std::nullopt, std::nullopt);
input_method_context_->OnPreeditString("xyz", {}, gfx::Range(1));
connection_->Flush();
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
zcr_extended_text_input_v1_send_confirm_preedit(
server->text_input_extension_v1()->extended_text_input()->resource(),
ZCR_EXTENDED_TEXT_INPUT_V1_CONFIRM_PREEDIT_SELECTION_BEHAVIOR_AFTER_PREEDIT);
});
EXPECT_THAT(input_method_context_delegate_->last_on_confirm_composition_arg(),
Optional(false));
EXPECT_EQ(input_method_context_->predicted_state_for_testing().selection,
gfx::Range(5));
EXPECT_EQ(input_method_context_->predicted_state_for_testing().composition,
gfx::Range(0));
}
TEST_P(WaylandInputMethodContextTest, OnConfirmCompositionTextForLongRange) {
const std::u16string text(5000, u'あ');
constexpr gfx::Range range(4000, 4500);
std::string expected_sent_text;
gfx::Range expected_sent_range;
if (GetApiVersion() == wl::TestZcrTextInputExtensionV1::Version::kV8) {
expected_sent_text = base::UTF16ToUTF8(std::u16string(1332, u'あ'));
expected_sent_range = gfx::Range(1248, 2748);
} else {
expected_sent_text = base::UTF16ToUTF8(std::u16string(832, u'あ'));
expected_sent_range = gfx::Range(498, 1998);
}
PostToServerAndWait([expected_sent_text, expected_sent_range](
wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->text_input_manager_v1()->text_input(),
SetSurroundingText(expected_sent_text, expected_sent_range));
});
input_method_context_->SetSurroundingText(text, gfx::Range(0, 5000),
gfx::Range::InvalidRange(), range,
std::nullopt, std::nullopt);
EXPECT_EQ(
input_method_context_->predicted_state_for_testing().surrounding_text,
text);
EXPECT_EQ(input_method_context_->predicted_state_for_testing().selection,
range);
connection_->Flush();
PostToServerAndWait([expected_sent_text, expected_sent_range](
wl::TestWaylandServerThread* server) {
auto* text_input = server->text_input_manager_v1()->text_input();
Mock::VerifyAndClearExpectations(text_input);
gfx::Range range =
gfx::Range(expected_sent_range.end(), expected_sent_range.start());
zwp_text_input_v1_send_cursor_position(text_input->resource(),
range.start(), range.end());
zwp_text_input_v1_send_commit_string(text_input->resource(), 0,
expected_sent_text.c_str());
});
EXPECT_THAT(input_method_context_delegate_->last_on_confirm_composition_arg(),
Optional(true));
EXPECT_EQ(
input_method_context_->predicted_state_for_testing().surrounding_text,
text);
EXPECT_EQ(input_method_context_->predicted_state_for_testing().selection,
range);
EXPECT_EQ(input_method_context_->predicted_state_for_testing().composition,
gfx::Range(0));
}
#endif
TEST_P(WaylandInputMethodContextTest, OnSetPreeditRegion_Success) { … }
TEST_P(WaylandInputMethodContextTest, OnSetPreeditRegion_NoSurroundingText) { … }
TEST_P(WaylandInputMethodContextTest,
OnSetPreeditRegion_GraphemeClusterIndependeceSimple) { … }
TEST_P(WaylandInputMethodContextTest,
OnSetPreeditRegion_GraphemeClusterIndependeceCombined) { … }
TEST_P(WaylandInputMethodContextTest, OnClearGrammarFragments) { … }
TEST_P(WaylandInputMethodContextTest, OnAddGrammarFragments) { … }
TEST_P(WaylandInputMethodContextTest, OnInsertImage) { … }
TEST_P(WaylandInputMethodContextTest, OnSetAutocorrectRange) { … }
TEST_P(WaylandInputMethodContextTest, OnSetVirtualKeyboardOccludedBounds) { … }
TEST_P(WaylandInputMethodContextTest,
OnSetVirtualKeyboardOccludedBoundsUpdatesPastTextInputClients) { … }
TEST_P(WaylandInputMethodContextTest,
OnSetVirtualKeyboardOccludedBoundsWithDeletedPastTextInputClient) { … }
TEST_P(WaylandInputMethodContextTest, DisplayVirtualKeyboard) { … }
TEST_P(WaylandInputMethodContextTest, DismissVirtualKeyboard) { … }
TEST_P(WaylandInputMethodContextTest, UpdateVirtualKeyboardState) { … }
TEST_P(WaylandInputMethodContextTest, OnKeySym) { … }
namespace {
std::unique_ptr<KeyEvent> CreateKeyEventForCharacterComposer(
KeyboardCode keyboard_code,
DomCode dom_code,
DomKey dom_key) { … }
}
TEST_P(WaylandInputMethodContextTest, CharacterComposerPreeditStringDeadKey) { … }
TEST_P(WaylandInputMethodContextTest,
CharacterComposerPreeditStringComposeKey) { … }
class WaylandInputMethodContextNoKeyboardTest
: public WaylandInputMethodContextTest { … };
INSTANTIATE_TEST_SUITE_P(…);
TEST_P(WaylandInputMethodContextNoKeyboardTest, ActivateDeactivate) { … }
TEST_P(WaylandInputMethodContextNoKeyboardTest, UpdateFocusBetweenTextFields) { … }
class WaylandInputMethodContextWithMockWrapperTest : public WaylandTestSimple { … };
TEST_F(WaylandInputMethodContextWithMockWrapperTest,
SetSurroundingShortTextWithCompositionRange) { … }
TEST_F(WaylandInputMethodContextWithMockWrapperTest,
SetSurroundingLongTextWithCompositionRange) { … }
TEST_F(WaylandInputMethodContextWithMockWrapperTest,
SetSurroundingLongTextWithCompositionRangeOutsideSurroundingTextRange) { … }
TEST_F(WaylandInputMethodContextWithMockWrapperTest,
SetSurroundingWithCompositionRangeOutideText) { … }
TEST_F(WaylandInputMethodContextWithMockWrapperTest,
SetSurroundingWithCompositionRangeInvalid) { … }
TEST_F(WaylandInputMethodContextWithMockWrapperTest, OnPreeditChanged) { … }
TEST_F(WaylandInputMethodContextWithMockWrapperTest,
OnPreeditChangedInvalidCursorEnd) { … }
TEST_F(WaylandInputMethodContextWithMockWrapperTest,
OnPreeditChangedGnomeWorkaround) { … }
}