chromium/chrome/browser/ash/input_method/longpress_diacritics_suggester_unittest.cc

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/input_method/longpress_diacritics_suggester.h"

#include <string>

#include "ash/constants/notifier_catalogs.h"
#include "ash/test/ash_test_base.h"
#include "base/containers/fixed_flat_map.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "chrome/browser/ash/input_method/fake_suggestion_handler.h"
#include "chrome/browser/ash/input_method/suggestion_enums.h"
#include "chrome/test/base/testing_profile.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/keycodes/dom/dom_code.h"

namespace ash {
namespace input_method {
namespace {

struct DiacriticsTestCase {
  char longpress_char;
  ui::DomCode code;
  bool is_shifted;
  std::u16string surrounding_text;
  std::u16string invalid_surrounding_text;
  std::vector<std::u16string> candidates;
};

class LongpressDiacriticsSuggesterTest
    : public AshTestBase,
      public testing::WithParamInterface<DiacriticsTestCase> {};

using AssistiveWindowButton = ui::ime::AssistiveWindowButton;

const int kContextId = 24601;
const char kUSEngineId[] = "xkb:us::eng";
const auto kDigitToDomCode = base::MakeFixedFlatMap<int, ui::DomCode>({
    {0, ui::DomCode::DIGIT0},
    {1, ui::DomCode::DIGIT1},
    {2, ui::DomCode::DIGIT2},
    {3, ui::DomCode::DIGIT3},
    {4, ui::DomCode::DIGIT4},
    {5, ui::DomCode::DIGIT5},
    {6, ui::DomCode::DIGIT6},
    {7, ui::DomCode::DIGIT7},
    {8, ui::DomCode::DIGIT8},
    {9, ui::DomCode::DIGIT9},
});

ui::KeyEvent CreateKeyEventFromCode(const ui::DomCode& code) {
  return ui::KeyEvent(ui::EventType::kKeyPressed, ui::VKEY_UNKNOWN, code,
                      ui::EF_NONE, ui::DomKey::NONE, ui::EventTimeForNow());
}

ui::KeyEvent CreateRepeatKeyEventFromCode(const ui::DomCode& code,
                                          bool shifted) {
  int flags = ui::EF_IS_REPEAT;
  if (shifted) {
    flags |= ui::EF_SHIFT_DOWN;
  }
  return ui::KeyEvent(ui::EventType::kKeyPressed, ui::VKEY_UNKNOWN, code, flags,
                      ui::DomKey::NONE, ui::EventTimeForNow());
}

// Required since FakeSuggestionHandler joins the candidates.
std::u16string Join(std::vector<std::u16string> candidates) {
  return base::JoinString(candidates, u";");
}

AssistiveWindowButton CreateDiacriticsButtonFor(
    size_t index,
    std::u16string announce_string) {
  AssistiveWindowButton button = {
      .id = ui::ime::ButtonId::kSuggestion,
      .window_type =
          ash::ime::AssistiveWindowType::kLongpressDiacriticsSuggestion,
      .suggestion_index = index,
      .announce_string = announce_string,
  };
  return button;
}
}  // namespace

TEST_P(LongpressDiacriticsSuggesterTest, SuggestsOnTrySuggest) {
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);

  EXPECT_TRUE(suggestion_handler.GetShowingSuggestion());
  EXPECT_EQ(suggestion_handler.GetSuggestionText(),
            Join(GetParam().candidates));
  EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
}

TEST_P(LongpressDiacriticsSuggesterTest,
       ShouldReturnFalseForInvalidTextChangeEvent) {
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);

  EXPECT_FALSE(suggester.TrySuggestWithSurroundingText(
      GetParam().invalid_surrounding_text,
      gfx::Range(GetParam().invalid_surrounding_text.size())));
}

TEST_P(LongpressDiacriticsSuggesterTest, DoesNotSuggestForInvalidEngineId) {
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId("xkb::someunsupportedengine");
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress('a');

  EXPECT_FALSE(suggestion_handler.GetShowingSuggestion());
  EXPECT_EQ(suggestion_handler.GetSuggestionText(), u"");
}

TEST_P(LongpressDiacriticsSuggesterTest, DoesNotSuggestForInvalidKeyChar) {
  FakeSuggestionHandler suggestion_handler;
  base::HistogramTester histogram_tester;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress('~');  // Char doesn't have diacritics.

  EXPECT_FALSE(suggestion_handler.GetShowingSuggestion());
  EXPECT_EQ(suggestion_handler.GetSuggestionText(), u"");
  histogram_tester.ExpectUniqueSample("Ash.NotifierFramework.Nudge.ShownCount",
                                      NudgeCatalogName::kDisableDiacritics, 1);
}

TEST_P(LongpressDiacriticsSuggesterTest, DoesNotSuggestAfterBlur) {
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.OnBlur();
  suggester.TrySuggestOnLongpress(GetParam().longpress_char);

  EXPECT_FALSE(suggestion_handler.GetShowingSuggestion());
  EXPECT_EQ(suggestion_handler.GetSuggestionText(), u"");
}

TEST_P(LongpressDiacriticsSuggesterTest, HighlightsFirstOnInitialNextKeyEvent) {
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ARROW_RIGHT));

  EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
  EXPECT_TRUE(suggestion_handler.GetShowingSuggestion());
  EXPECT_EQ(suggestion_handler.GetSuggestionText(),
            Join(GetParam().candidates));
  EXPECT_EQ(suggestion_handler.GetHighlightedButton(),
            CreateDiacriticsButtonFor(0, GetParam().candidates[0]));
}

TEST_P(LongpressDiacriticsSuggesterTest,
       HighlightsSettingsOnInitialPreviousKeyEvent) {
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ARROW_LEFT));

  AssistiveWindowButton learn_more_button = {
      .id = ui::ime::ButtonId::kLearnMore,
      .window_type =
          ash::ime::AssistiveWindowType::kLongpressDiacriticsSuggestion,
  };

  EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
  EXPECT_TRUE(suggestion_handler.GetShowingSuggestion());
  EXPECT_EQ(suggestion_handler.GetSuggestionText(),
            Join(GetParam().candidates));
  EXPECT_EQ(suggestion_handler.GetHighlightedButton(), learn_more_button);
}

TEST_P(LongpressDiacriticsSuggesterTest, HighlightIncrementsOnNextKeyEvent) {
  size_t expected_candidate_index = 2 % GetParam().candidates.size();
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ARROW_RIGHT));
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ARROW_RIGHT));
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ARROW_RIGHT));

  EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
  EXPECT_TRUE(suggestion_handler.GetShowingSuggestion());
  EXPECT_EQ(suggestion_handler.GetSuggestionText(),
            Join(GetParam().candidates));
  EXPECT_EQ(suggestion_handler.GetHighlightedButton(),
            CreateDiacriticsButtonFor(
                expected_candidate_index,
                GetParam().candidates[expected_candidate_index]));
}

TEST_P(LongpressDiacriticsSuggesterTest, HighlightIncrementsOnTabKeyEvent) {
  size_t expected_candidate_index = 2 % GetParam().candidates.size();
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::TAB));
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::TAB));
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::TAB));

  EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
  EXPECT_TRUE(suggestion_handler.GetShowingSuggestion());
  EXPECT_EQ(suggestion_handler.GetSuggestionText(),
            Join(GetParam().candidates));
  EXPECT_EQ(suggestion_handler.GetHighlightedButton(),
            CreateDiacriticsButtonFor(
                expected_candidate_index,
                GetParam().candidates[expected_candidate_index]));
}

TEST_P(LongpressDiacriticsSuggesterTest,
       HighlightDecrementsOnPreviousKeyEvent) {
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ARROW_RIGHT));
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ARROW_RIGHT));
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ARROW_LEFT));

  EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
  EXPECT_TRUE(suggestion_handler.GetShowingSuggestion());
  EXPECT_EQ(suggestion_handler.GetSuggestionText(),
            Join(GetParam().candidates));
  EXPECT_EQ(suggestion_handler.GetHighlightedButton(),
            CreateDiacriticsButtonFor(0, GetParam().candidates[0]));
}

TEST_P(LongpressDiacriticsSuggesterTest,
       HighlightWrapsAroundAfterLastIndexOnNextKeyEvent) {
  size_t expected_candidate_index = 9 % (GetParam().candidates.size() + 1);
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  for (int i = 0; i < 10; i++) {
    suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ARROW_RIGHT));
  }

  AssistiveWindowButton learn_more_button = {
      .id = ui::ime::ButtonId::kLearnMore,
      .window_type =
          ash::ime::AssistiveWindowType::kLongpressDiacriticsSuggestion,
  };
  AssistiveWindowButton expectedButton =
      expected_candidate_index == GetParam().candidates.size()
          ? learn_more_button
          : CreateDiacriticsButtonFor(
                expected_candidate_index,
                GetParam().candidates[expected_candidate_index]);

  EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
  EXPECT_TRUE(suggestion_handler.GetShowingSuggestion());
  EXPECT_EQ(suggestion_handler.GetSuggestionText(),
            Join(GetParam().candidates));
  EXPECT_EQ(suggestion_handler.GetHighlightedButton(), expectedButton);
}

TEST_P(LongpressDiacriticsSuggesterTest,
       HighlightWrapsAroundAfterFirstIndexOnPreviousKeyEvent) {
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ARROW_RIGHT));
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ARROW_LEFT));

  AssistiveWindowButton learn_more_button = {
      .id = ui::ime::ButtonId::kLearnMore,
      .window_type =
          ash::ime::AssistiveWindowType::kLongpressDiacriticsSuggestion,
  };

  EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
  EXPECT_TRUE(suggestion_handler.GetShowingSuggestion());
  EXPECT_EQ(suggestion_handler.GetSuggestionText(),
            Join(GetParam().candidates));
  EXPECT_EQ(suggestion_handler.GetHighlightedButton(), learn_more_button);
}

TEST_P(LongpressDiacriticsSuggesterTest,
       ResetsHighlightsAfterBlurForNextKeyEvent) {
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);
  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ARROW_RIGHT));
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ARROW_RIGHT));
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ARROW_RIGHT));
  suggester.OnBlur();
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ARROW_RIGHT));

  EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
  EXPECT_TRUE(suggestion_handler.GetShowingSuggestion());
  EXPECT_EQ(suggestion_handler.GetSuggestionText(),
            Join(GetParam().candidates));
  EXPECT_EQ(suggestion_handler.GetHighlightedButton(),
            CreateDiacriticsButtonFor(0, GetParam().candidates[0]));
}

TEST_P(LongpressDiacriticsSuggesterTest,
       ResetsHighlightsAfterAcceptForNextKeyEvent) {
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);
  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ARROW_RIGHT));
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ARROW_RIGHT));
  suggester.AcceptSuggestion(1);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ARROW_RIGHT));

  EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
  EXPECT_TRUE(suggestion_handler.GetShowingSuggestion());
  EXPECT_EQ(suggestion_handler.GetSuggestionText(),
            Join(GetParam().candidates));
  EXPECT_EQ(suggestion_handler.GetHighlightedButton(),
            CreateDiacriticsButtonFor(0, GetParam().candidates[0]));
}

TEST_P(LongpressDiacriticsSuggesterTest,
       ResetsHighlightsAfterDismissForNextKeyEvent) {
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);
  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ARROW_RIGHT));
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ARROW_RIGHT));
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ARROW_RIGHT));
  suggester.DismissSuggestion();

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ARROW_RIGHT));

  EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
  EXPECT_TRUE(suggestion_handler.GetShowingSuggestion());
  EXPECT_EQ(suggestion_handler.GetSuggestionText(),
            Join(GetParam().candidates));
  EXPECT_EQ(suggestion_handler.GetHighlightedButton(),
            CreateDiacriticsButtonFor(0, GetParam().candidates[0]));
}

TEST_P(LongpressDiacriticsSuggesterTest,
       ResetsHighlightsAfterFocusChangeForNextKeyEvent) {
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(1);
  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ARROW_RIGHT));
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ARROW_RIGHT));
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ARROW_RIGHT));
  suggester.OnFocus(2);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ARROW_RIGHT));

  EXPECT_EQ(suggestion_handler.GetContextId(), 2);
  EXPECT_TRUE(suggestion_handler.GetShowingSuggestion());
  EXPECT_EQ(suggestion_handler.GetSuggestionText(),
            Join(GetParam().candidates));
  EXPECT_EQ(suggestion_handler.GetHighlightedButton(),
            CreateDiacriticsButtonFor(0, GetParam().candidates[0]));
}

TEST_P(LongpressDiacriticsSuggesterTest, AcceptsOnEnterKeyPress) {
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ARROW_RIGHT));
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ENTER));

  EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
  EXPECT_FALSE(suggestion_handler.GetShowingSuggestion());
  EXPECT_EQ(suggestion_handler.GetAcceptedSuggestionText(),
            GetParam().candidates[0]);
  EXPECT_EQ(suggestion_handler.GetDeletePreviousUtf16Len(), 1u);
}

TEST_P(LongpressDiacriticsSuggesterTest, NotHandledOnDigit0KeyPress) {
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::DIGIT0));

  EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
  EXPECT_FALSE(suggestion_handler.GetAcceptedSuggestion());
}

TEST_P(LongpressDiacriticsSuggesterTest, HandlesDigit1KeyPress) {
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::DIGIT1));

  EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
  EXPECT_FALSE(suggestion_handler.GetShowingSuggestion());
  EXPECT_EQ(suggestion_handler.GetAcceptedSuggestionText(),
            GetParam().candidates[0]);
  EXPECT_EQ(suggestion_handler.GetDeletePreviousUtf16Len(), 1u);
}

TEST_P(LongpressDiacriticsSuggesterTest, HandlesDigit2KeyPress) {
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::DIGIT2));

  if (GetParam().candidates.size() < 2) {
    EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
    EXPECT_FALSE(suggestion_handler.GetAcceptedSuggestion());
  } else {
    EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
    EXPECT_FALSE(suggestion_handler.GetShowingSuggestion());
    EXPECT_EQ(suggestion_handler.GetAcceptedSuggestionText(),
              GetParam().candidates[1]);
    EXPECT_EQ(suggestion_handler.GetDeletePreviousUtf16Len(), 1u);
  }
}

TEST_P(LongpressDiacriticsSuggesterTest, HandlesDigit3KeyPress) {
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::DIGIT3));

  if (GetParam().candidates.size() < 3) {
    EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
    EXPECT_FALSE(suggestion_handler.GetAcceptedSuggestion());
  } else {
    EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
    EXPECT_FALSE(suggestion_handler.GetShowingSuggestion());
    EXPECT_EQ(suggestion_handler.GetAcceptedSuggestionText(),
              GetParam().candidates[2]);
    EXPECT_EQ(suggestion_handler.GetDeletePreviousUtf16Len(), 1u);
  }
}

TEST_P(LongpressDiacriticsSuggesterTest, HandlesDigit4KeyPress) {
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::DIGIT4));

  if (GetParam().candidates.size() < 4) {
    EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
    EXPECT_FALSE(suggestion_handler.GetAcceptedSuggestion());
  } else {
    EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
    EXPECT_FALSE(suggestion_handler.GetShowingSuggestion());
    EXPECT_EQ(suggestion_handler.GetAcceptedSuggestionText(),
              GetParam().candidates[3]);
    EXPECT_EQ(suggestion_handler.GetDeletePreviousUtf16Len(), 1u);
  }
}

TEST_P(LongpressDiacriticsSuggesterTest, HandlesDigit5KeyPress) {
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::DIGIT5));

  if (GetParam().candidates.size() < 5) {
    EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
    EXPECT_FALSE(suggestion_handler.GetAcceptedSuggestion());
  } else {
    EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
    EXPECT_FALSE(suggestion_handler.GetShowingSuggestion());
    EXPECT_EQ(suggestion_handler.GetAcceptedSuggestionText(),
              GetParam().candidates[4]);
    EXPECT_EQ(suggestion_handler.GetDeletePreviousUtf16Len(), 1u);
  }
}

TEST_P(LongpressDiacriticsSuggesterTest, HandlesDigit6KeyPress) {
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::DIGIT6));

  if (GetParam().candidates.size() < 6) {
    EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
    EXPECT_FALSE(suggestion_handler.GetAcceptedSuggestion());
  } else {
    EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
    EXPECT_FALSE(suggestion_handler.GetShowingSuggestion());
    EXPECT_EQ(suggestion_handler.GetAcceptedSuggestionText(),
              GetParam().candidates[5]);
    EXPECT_EQ(suggestion_handler.GetDeletePreviousUtf16Len(), 1u);
  }
}

TEST_P(LongpressDiacriticsSuggesterTest, HandlesDigit7KeyPress) {
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::DIGIT7));

  if (GetParam().candidates.size() < 7) {
    EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
    EXPECT_FALSE(suggestion_handler.GetAcceptedSuggestion());
  } else {
    EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
    EXPECT_FALSE(suggestion_handler.GetShowingSuggestion());
    EXPECT_EQ(suggestion_handler.GetAcceptedSuggestionText(),
              GetParam().candidates[6]);
    EXPECT_EQ(suggestion_handler.GetDeletePreviousUtf16Len(), 1u);
  }
}

TEST_P(LongpressDiacriticsSuggesterTest, HandlesDigit8KeyPress) {
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::DIGIT8));

  if (GetParam().candidates.size() < 8) {
    EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
    EXPECT_FALSE(suggestion_handler.GetAcceptedSuggestion());
  } else {
    EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
    EXPECT_FALSE(suggestion_handler.GetShowingSuggestion());
    EXPECT_EQ(suggestion_handler.GetAcceptedSuggestionText(),
              GetParam().candidates[7]);
    EXPECT_EQ(suggestion_handler.GetDeletePreviousUtf16Len(), 1u);
  }
}

TEST_P(LongpressDiacriticsSuggesterTest,
       NotHandledOnEnterKeyPressIfNoHighlight) {
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ENTER));

  EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
  EXPECT_FALSE(suggestion_handler.GetAcceptedSuggestion());
}

TEST_P(LongpressDiacriticsSuggesterTest, DismissSuggestionOnEscKeyPress) {
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ARROW_RIGHT));

  EXPECT_EQ(
      suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ESCAPE)),
      SuggestionStatus::kDismiss);
  EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
  EXPECT_FALSE(suggestion_handler.GetShowingSuggestion());
}

TEST_P(LongpressDiacriticsSuggesterTest, DismissSuggestionOnSecondKeyPress) {
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);

  EXPECT_EQ(suggester.HandleKeyEvent(CreateKeyEventFromCode(GetParam().code)),
            SuggestionStatus::kNotHandled);
  EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
  EXPECT_FALSE(suggestion_handler.GetShowingSuggestion());
  EXPECT_FALSE(suggestion_handler.GetAcceptedSuggestion());
}

TEST_P(LongpressDiacriticsSuggesterTest,
       AcceptSuggestionOnSecondKeyPressIfHighlighted) {
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ARROW_RIGHT));

  EXPECT_EQ(suggester.HandleKeyEvent(CreateKeyEventFromCode(GetParam().code)),
            SuggestionStatus::kNotHandled);
  EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
  EXPECT_FALSE(suggestion_handler.GetShowingSuggestion());
  EXPECT_EQ(suggestion_handler.GetAcceptedSuggestionText(),
            GetParam().candidates[0]);
  EXPECT_EQ(suggestion_handler.GetDeletePreviousUtf16Len(), 1u);
}

TEST_P(LongpressDiacriticsSuggesterTest, NoDismissSuggestionOnRepeatKeyPress) {
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);

  EXPECT_EQ(suggester.HandleKeyEvent(CreateRepeatKeyEventFromCode(
                GetParam().code, GetParam().is_shifted)),
            SuggestionStatus::kNotHandled);
  EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
  EXPECT_TRUE(suggestion_handler.GetShowingSuggestion());
  EXPECT_FALSE(suggestion_handler.GetAcceptedSuggestion());
}

TEST_P(LongpressDiacriticsSuggesterTest, ReturnsDiacriticsProposeActionType) {
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  EXPECT_EQ(suggester.GetProposeActionType(),
            AssistiveType::kLongpressDiacritics);
}

TEST_P(LongpressDiacriticsSuggesterTest, RecordsAcceptanceCharCodeMetric) {
  base::HistogramTester histogram_tester;

  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  int histogram_accept_count = 0;
  for (size_t i = 0; i < 9 && i < GetParam().candidates.size(); i++) {
    // Insert using dom code for index + 1 (i.e. DIGIT1 inserts 0th index
    // candidate)
    ui::DomCode dom_code = kDigitToDomCode.find(i + 1)->second;
    suggester.TrySuggestOnLongpress(GetParam().longpress_char);
    suggester.HandleKeyEvent(CreateKeyEventFromCode(dom_code));

    histogram_tester.ExpectTotalCount(
        "InputMethod.PhysicalKeyboard.LongpressDiacritics.AcceptedChar",
        ++histogram_accept_count);
    int char_code = int(GetParam().candidates[i][0]);
    histogram_tester.ExpectBucketCount(
        "InputMethod.PhysicalKeyboard.LongpressDiacritics.AcceptedChar",
        char_code, 1);
  }
}

TEST_P(LongpressDiacriticsSuggesterTest, RecordsShowWindowActionMetric) {
  base::HistogramTester histogram_tester;
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);

  histogram_tester.ExpectUniqueSample(
      "InputMethod.PhysicalKeyboard.LongpressDiacritics.Action",
      IMEPKLongpressDiacriticAction::kShowWindow, 1);
}

TEST_P(LongpressDiacriticsSuggesterTest, RecordsAcceptActionMetric) {
  base::HistogramTester histogram_tester;
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::DIGIT1));

  histogram_tester.ExpectBucketCount(
      "InputMethod.PhysicalKeyboard.LongpressDiacritics.Action",
      IMEPKLongpressDiacriticAction::kAccept, 1);
}

TEST_P(LongpressDiacriticsSuggesterTest, RecordsDismissActionMetricOnEsc) {
  base::HistogramTester histogram_tester;
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ESCAPE));

  histogram_tester.ExpectBucketCount(
      "InputMethod.PhysicalKeyboard.LongpressDiacritics.Action",
      IMEPKLongpressDiacriticAction::kDismiss, 1);
}

TEST_P(LongpressDiacriticsSuggesterTest,
       RecordsDismissActionMetricOnOtherKeyPress) {
  base::HistogramTester histogram_tester;
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(GetParam().code));

  histogram_tester.ExpectBucketCount(
      "InputMethod.PhysicalKeyboard.LongpressDiacritics.Action",
      IMEPKLongpressDiacriticAction::kDismiss, 1);
}

TEST_P(LongpressDiacriticsSuggesterTest, A11yAnnounceOnShowWindow) {
  base::HistogramTester histogram_tester;
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);

  ASSERT_EQ(suggestion_handler.GetAnnouncements().size(), 1u);
  EXPECT_EQ(suggestion_handler.GetAnnouncements().front(),
            u"Accent marks menu open. Press left, right, or number keys to "
            u"navigate and enter to insert.");
}

TEST_P(LongpressDiacriticsSuggesterTest, A11yAnnounceOnDismissWithEsc) {
  base::HistogramTester histogram_tester;
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ESCAPE));

  ASSERT_EQ(suggestion_handler.GetAnnouncements().size(), 2u);
  EXPECT_EQ(suggestion_handler.GetAnnouncements().front(),
            u"Accent marks menu open. Press left, right, or number keys to "
            u"navigate and enter to insert.");
  EXPECT_EQ(suggestion_handler.GetAnnouncements().back(),
            u"Accent marks menu dismissed.");
}

TEST_P(LongpressDiacriticsSuggesterTest, A11yAnnounceOnDismissByTyping) {
  base::HistogramTester histogram_tester;
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(GetParam().code));

  ASSERT_EQ(suggestion_handler.GetAnnouncements().size(), 2u);
  EXPECT_EQ(suggestion_handler.GetAnnouncements().front(),
            u"Accent marks menu open. Press left, right, or number keys to "
            u"navigate and enter to insert.");
  EXPECT_EQ(suggestion_handler.GetAnnouncements().back(),
            u"Accent marks menu dismissed.");
}

TEST_P(LongpressDiacriticsSuggesterTest, A11yAnnounceOnAcceptViaDigit) {
  base::HistogramTester histogram_tester;
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::DIGIT1));

  ASSERT_EQ(suggestion_handler.GetAnnouncements().size(), 2u);
  EXPECT_EQ(suggestion_handler.GetAnnouncements().front(),
            u"Accent marks menu open. Press left, right, or number keys to "
            u"navigate and enter to insert.");
  EXPECT_EQ(suggestion_handler.GetAnnouncements().back(),
            u"Accent mark inserted.");
}

TEST_P(LongpressDiacriticsSuggesterTest, A11yAnnounceOnAcceptViaEnter) {
  base::HistogramTester histogram_tester;
  FakeSuggestionHandler suggestion_handler;
  LongpressDiacriticsSuggester suggester =
      LongpressDiacriticsSuggester(&suggestion_handler);
  suggester.SetEngineId(kUSEngineId);
  suggester.OnFocus(kContextId);

  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ARROW_RIGHT));
  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::ENTER));

  ASSERT_EQ(suggestion_handler.GetAnnouncements().size(), 2u);
  EXPECT_EQ(suggestion_handler.GetAnnouncements().front(),
            u"Accent marks menu open. Press left, right, or number keys to "
            u"navigate and enter to insert.");
  EXPECT_EQ(suggestion_handler.GetAnnouncements().back(),
            u"Accent mark inserted.");
}

INSTANTIATE_TEST_SUITE_P(
    /* no prefix */,
    LongpressDiacriticsSuggesterTest,
    testing::ValuesIn<DiacriticsTestCase>(
        {{'a',
          ui::DomCode::US_A,
          false,
          u"ca",
          u"caf",
          {u"à", u"á", u"â", u"ä", u"æ", u"ã", u"å", u"ā"}},
         {'A',
          ui::DomCode::US_A,
          true,
          u"cA",
          u"cAf",
          {u"À", u"Á", u"Â", u"Ä", u"Æ", u"Ã", u"Å", u"Ā"}},
         {'c', ui::DomCode::US_C, false, u"c", u"ca", {u"ç"}},
         {'C', ui::DomCode::US_C, true, u"C", u"Ca", {u"Ç"}},
         {'e',
          ui::DomCode::US_E,
          false,
          u"soufle",
          u"soufles",
          {u"é", u"è", u"ê", u"ë", u"ē"}},
         {'E',
          ui::DomCode::US_E,
          true,
          u"SOUFLE",
          u"SOUFLES",
          {u"É", u"È", u"Ê", u"Ë", u"Ē"}}}),
    [](const testing::TestParamInfo<DiacriticsTestCase>& info) {
      return std::string(1, info.param.longpress_char);
    });

}  // namespace input_method
}  // namespace ash