text: // Copyright 2018 The Chromium 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 <string>
  #include <vector>
  
  #include "base/containers/flat_map.h"
  #include "base/macros.h"
  #include "base/stl_util.h"
  #include "testing/gtest/include/gtest/gtest.h"
  #include "ui/events/keycodes/dom/dom_key.h"
  #include "ui/events/keycodes/dom/dom_keyboard_layout.h"
  #include "ui/events/keycodes/dom/dom_keyboard_layout_manager.h"
  #include "ui/events/keycodes/dom/dom_keyboard_layout_map_base.h"
  #include "ui/events/keycodes/dom/keycode_converter.h"
  
  namespace ui {
  
  namespace {
  
  // Represents a keyboard layout where every key is ASCII capable.
  constexpr uint32_t kKeyboardLayoutWithAllValidKeys = 1;
  // Layout contains all valid keys so this number represents a complete layout.
  constexpr uint32_t kKeyboardLayoutWithAllValidKeysCount = 50;
  
  // Represents a keyboard layout where every alpha key is ASCII capable.
  constexpr uint32_t kKeyboardLayoutWithAllValidAlphaKeys = 2;
  // Layout contains all valid alpha keys so this number represents a complete
  // layout minus two INTL keys which are subbed.
  constexpr uint32_t kKeyboardLayoutWithAllValidAlphaKeysCount = 48;
  
  // Represents a keyboard layout where some alpha keys are ASCII capable.
  constexpr uint32_t kKeyboardLayoutWithSomeValidKeys = 3;
  // The mostly valid keyboard does not pass the 'IsAsciiCapable()' test however
  // the DomKeyboardLayoutManager will pass the first layout it has if it doesn't
  // find an ASCII capable layout first.
  constexpr uint32_t kKeyboardLayoutWithSomeValidKeysCount = 47;
  
  // Represents a keyboard layout where no alpha keys are ASCII capable.
  constexpr uint32_t kKeyboardLayoutWithNoValidKeys = 4;
  constexpr uint32_t kKeyboardLayoutWithNoValidKeysCount = 0;
  
  DomKey GetKeyFromFullLookupTable(DomCode dom_code) {
    // Includes all DomCodes used in |writing_system_key_domcodes|.  Tests can
    // use this table to retrieve valid DomKeys for each DomCode to simulate a
    // 'complete' keyboard layout.  They can also use this to construct a partial
    // layout with valid DomKey values for the populated entries.
    static base::flat_map<DomCode, DomKey> kFullLookupTable(
        {{DomCode::DIGIT0, DomKey::Constant<'0'>::Character},
         {DomCode::DIGIT1, DomKey::Constant<'1'>::Character},
         {DomCode::DIGIT2, DomKey::Constant<'2'>::Character},
         {DomCode::DIGIT3, DomKey::Constant<'3'>::Character},
         {DomCode::DIGIT4, DomKey::Constant<'4'>::Character},
         {DomCode::DIGIT5, DomKey::Constant<'5'>::Character},
         {DomCode::DIGIT6, DomKey::Constant<'6'>::Character},
         {DomCode::DIGIT7, DomKey::Constant<'7'>::Character},
         {DomCode::DIGIT8, DomKey::Constant<'8'>::Character},
         {DomCode::DIGIT9, DomKey::Constant<'9'>::Character},
         {DomCode::US_A, DomKey::Constant<'a'>::Character},
         {DomCode::US_B, DomKey::Constant<'b'>::Character},
         {DomCode::US_C, DomKey::Constant<'c'>::Character},
         {DomCode::US_D, DomKey::Constant<'d'>::Character},
         {DomCode::US_E, DomKey::Constant<'e'>::Character},
         {DomCode::US_F, DomKey::Constant<'f'>::Character},
         {DomCode::US_G, DomKey::Constant<'g'>::Character},
         {DomCode::US_H, DomKey::Constant<'h'>::Character},
         {DomCode::US_I, DomKey::Constant<'i'>::Character},
         {DomCode::US_J, DomKey::Constant<'j'>::Character},
         {DomCode::US_K, DomKey::Constant<'k'>::Character},
         {DomCode::US_L, DomKey::Constant<'l'>::Character},
         {DomCode::US_M, DomKey::Constant<'m'>::Character},
         {DomCode::US_N, DomKey::Constant<'n'>::Character},
         {DomCode::US_O, DomKey::Constant<'o'>::Character},
         {DomCode::US_P, DomKey::Constant<'p'>::Character},
         {DomCode::US_Q, DomKey::Constant<'q'>::Character},
         {DomCode::US_R, DomKey::Constant<'r'>::Character},
         {DomCode::US_S, DomKey::Constant<'s'>::Character},
         {DomCode::US_T, DomKey::Constant<'t'>::Character},
         {DomCode::US_U, DomKey::Constant<'u'>::Character},
         {DomCode::US_V, DomKey::Constant<'v'>::Character},
         {DomCode::US_W, DomKey::Constant<'w'>::Character},
         {DomCode::US_X, DomKey::Constant<'x'>::Character},
         {DomCode::US_Y, DomKey::Constant<'y'>::Character},
         {DomCode::US_Z, DomKey::Constant<'z'>::Character},
         {DomCode::BACKQUOTE, DomKey::Constant<'`'>::Character},
         {DomCode::MINUS, DomKey::Constant<'-'>::Character},
         {DomCode::EQUAL, DomKey::Constant<'='>::Character},
         {DomCode::INTL_YEN, DomKey::Constant<0x00A5>::Dead},
         {DomCode::BRACKET_LEFT, DomKey::Constant<'{'>::Character},
         {DomCode::BRACKET_RIGHT, DomKey::Constant<'}'>::Character},
         {DomCode::BACKSLASH, DomKey::Constant<'\\'>::Character},
         {DomCode::SEMICOLON, DomKey::Constant<';'>::Character},
         {DomCode::QUOTE, DomKey::Constant<'\''>::Character},
         {DomCode::INTL_BACKSLASH, DomKey::Constant<'/'>::Character},
         {DomCode::COMMA, DomKey::Constant<','>::Character},
         {DomCode::PERIOD, DomKey::Constant<'.'>::Character},
         {DomCode::SLASH, DomKey::Constant<'/'>::Character},
         {DomCode::INTL_RO, DomKey::Constant<0x308D>::Dead}});
  
    // Ensure the 'full' lookup table contains the same number of elements as the
    // writing system table used by the class under test.  Ideally this would be a
    // static assert however that doesn't work since the other table is in a
    // different compilation unit.
    DCHECK_EQ(base::size(kFullLookupTable), kWritingSystemKeyDomCodeEntries);
  
    if (kFullLookupTable.count(dom_code) == 0)
      return DomKey::NONE;
  
    return kFullLookupTable[dom_code];
  }
  
  DomKey GetKeyFromCombiningLayoutTable(DomCode dom_code) {
    // Used for testing combining keys in both printable and combining forms.
    static base::flat_map<DomCode, DomKey> kCombiningLayoutTable({
        // Grave, combining.
        {DomCode::DIGIT0, DomKey::Constant<0x0300>::Dead},
        // Grave, printable.
        {DomCode::DIGIT1, DomKey::Constant<0x0060>::Character},
        // Acute, combining.
        {DomCode::DIGIT2, DomKey::Constant<0x0301>::Dead},
        // Acute, printable.
        {DomCode::DIGIT3, DomKey::Constant<0x0027>::Character},
        // Circumflex, combining.
        {DomCode::DIGIT4, DomKey::Constant<0x0302>::Dead},
        // Circumflex, printable.
        {DomCode::DIGIT5, DomKey::Constant<0x005e>::Character},
        // Tilde, combining.
        {DomCode::DIGIT6, DomKey::Constant<0x0303>::Dead},
        // Tilde, printable.
        {DomCode::DIGIT7, DomKey::Constant<0x007e>::Character},
        // Diaeresis, combining.
        {DomCode::DIGIT8, DomKey::Constant<0x0308>::Dead},
        // Diaeresis, printable.
        {DomCode::DIGIT9, DomKey::Constant<0x00a8>::Character},
    });
  
    if (kCombiningLayoutTable.count(dom_code) == 0)
      return DomKey::NONE;
  
    return kCombiningLayoutTable[dom_code];
  }
  
  }  // namespace
  
  class TestDomKeyboardLayoutMap : public DomKeyboardLayoutMapBase {
   public:
    TestDomKeyboardLayoutMap();
    ~TestDomKeyboardLayoutMap() override;
  
    // DomKeyboardLayoutMapBase overrides.
    uint32_t GetKeyboardLayoutCount() override;
    DomKey GetDomKeyFromDomCodeForLayout(DomCode dom_code,
                                         uint32_t keyboard_layout_id) override;
  
    // Adds a new keyboard layout in FIFO order.
    void AddKeyboardLayout(uint32_t test_layout_id);
  
   private:
    // Helper methods used to populate a layout for testing.
    DomKey GetDomKeyForLayoutWithAllValidKeys(DomCode dom_code);
    DomKey GetDomKeyForLayoutWithAllValidAlphaKeys(DomCode dom_code);
    DomKey GetDomKeyForLayoutWithSomeValidKeys(DomCode dom_code);
    DomKey GetDomKeyForLayoutWithNoValidKeys();
  
    std::vector<uint32_t> test_keyboard_layouts_;
  
    DISALLOW_COPY_AND_ASSIGN(TestDomKeyboardLayoutMap);
  };
  
  TestDomKeyboardLayoutMap::TestDomKeyboardLayoutMap() = default;
  
  TestDomKeyboardLayoutMap::~TestDomKeyboardLayoutMap() = default;
  
  uint32_t TestDomKeyboardLayoutMap::GetKeyboardLayoutCount() {
    return test_keyboard_layouts_.size();
  }
  
  DomKey TestDomKeyboardLayoutMap::GetDomKeyFromDomCodeForLayout(
      DomCode dom_code,
      uint32_t keyboard_layout_id) {
    uint32_t test_layout_id = test_keyboard_layouts_[keyboard_layout_id];
    if (test_layout_id == kKeyboardLayoutWithAllValidKeys)
      return GetDomKeyForLayoutWithAllValidKeys(dom_code);
    if (test_layout_id == kKeyboardLayoutWithAllValidAlphaKeys)
      return GetDomKeyForLayoutWithAllValidAlphaKeys(dom_code);
    if (test_layout_id == kKeyboardLayoutWithSomeValidKeys)
      return GetDomKeyForLayoutWithSomeValidKeys(dom_code);
    if (test_layout_id == kKeyboardLayoutWithNoValidKeys)
      return GetDomKeyForLayoutWithNoValidKeys();
  
    NOTREACHED();
    return DomKey::NONE;
  }
  
  void TestDomKeyboardLayoutMap::AddKeyboardLayout(uint32_t test_layout_id) {
    test_keyboard_layouts_.push_back(test_layout_id);
  }
  
  DomKey TestDomKeyboardLayoutMap::GetDomKeyForLayoutWithAllValidKeys(
      DomCode dom_code) {
    return GetKeyFromFullLookupTable(dom_code);
  }
  
  DomKey TestDomKeyboardLayoutMap::GetDomKeyForLayoutWithAllValidAlphaKeys(
      DomCode dom_code) {
    // If the number of excluded keys changes, please modify
    // |kKeyboardLayoutWithAllValidAlphaKeysCount| to match the new value.
    if (dom_code == DomCode::INTL_RO || dom_code == DomCode::INTL_YEN)
      return DomKey::NONE;
  
    // DIGIT 0 - 9 are overridden for combining char tests so use those here since
    // this method only ensures the alpha keys are valid.
    if (GetKeyFromCombiningLayoutTable(dom_code) != DomKey::NONE)
      return GetKeyFromCombiningLayoutTable(dom_code);
  
    return GetKeyFromFullLookupTable(dom_code);
  }
  
  DomKey TestDomKeyboardLayoutMap::GetDomKeyForLayoutWithSomeValidKeys(
      DomCode dom_code) {
    if (dom_code == DomCode::US_A || dom_code == DomCode::US_Z ||
        dom_code == DomCode::BACKQUOTE)
      return DomKey::NONE;
    return GetKeyFromFullLookupTable(dom_code);
  }
  
  DomKey TestDomKeyboardLayoutMap::GetDomKeyForLayoutWithNoValidKeys() {
    return DomKey::NONE;
  }
  
  TEST(DomKeyboardLayoutMapTest, MapGenerationWithZeroLayouts) {
    TestDomKeyboardLayoutMap test_keyboard_layout_map;
    auto map = test_keyboard_layout_map.Generate();
    ASSERT_EQ(0UL, map.size());
  }
  
  TEST(DomKeyboardLayoutMapTest, MapGenerationWithCompletelyValidLayout) {
    TestDomKeyboardLayoutMap test_keyboard_layout_map;
    test_keyboard_layout_map.AddKeyboardLayout(kKeyboardLayoutWithAllValidKeys);
  
    auto map = test_keyboard_layout_map.Generate();
    ASSERT_EQ(kKeyboardLayoutWithAllValidKeysCount, map.size());
  }
  
  TEST(DomKeyboardLayoutMapTest, MapGenerationWithValidAlphaKeys) {
    TestDomKeyboardLayoutMap test_keyboard_layout_map;
    test_keyboard_layout_map.AddKeyboardLayout(
        kKeyboardLayoutWithAllValidAlphaKeys);
  
    auto map = test_keyboard_layout_map.Generate();
    ASSERT_EQ(kKeyboardLayoutWithAllValidAlphaKeysCount, map.size());
  }
  
  TEST(DomKeyboardLayoutMapTest, MapGenerationWithMostlyValidAlphaKeys) {
    TestDomKeyboardLayoutMap test_keyboard_layout_map;
    test_keyboard_layout_map.AddKeyboardLayout(kKeyboardLayoutWithSomeValidKeys);
  
    auto map = test_keyboard_layout_map.Generate();
    ASSERT_EQ(kKeyboardLayoutWithSomeValidKeysCount, map.size());
  }
  
  TEST(DomKeyboardLayoutMapTest, MapGenerationWithNoValidKeys) {
    TestDomKeyboardLayoutMap test_keyboard_layout_map;
    test_keyboard_layout_map.AddKeyboardLayout(kKeyboardLayoutWithNoValidKeys);
  
    auto map = test_keyboard_layout_map.Generate();
    ASSERT_EQ(kKeyboardLayoutWithNoValidKeysCount, map.size());
  }
  
  TEST(DomKeyboardLayoutMapTest, MapGenerationWithValidLayoutFirst) {
    TestDomKeyboardLayoutMap test_keyboard_layout_map;
    test_keyboard_layout_map.AddKeyboardLayout(kKeyboardLayoutWithAllValidKeys);
    test_keyboard_layout_map.AddKeyboardLayout(kKeyboardLayoutWithSomeValidKeys);
    test_keyboard_layout_map.AddKeyboardLayout(kKeyboardLayoutWithNoValidKeys);
  
    auto map = test_keyboard_layout_map.Generate();
    ASSERT_EQ(kKeyboardLayoutWithAllValidKeysCount, map.size());
  }
  
  TEST(DomKeyboardLayoutMapTest, MapGenerationWithValidLayoutLast) {
    TestDomKeyboardLayoutMap test_keyboard_layout_map;
    test_keyboard_layout_map.AddKeyboardLayout(kKeyboardLayoutWithSomeValidKeys);
    test_keyboard_layout_map.AddKeyboardLayout(kKeyboardLayoutWithNoValidKeys);
    test_keyboard_layout_map.AddKeyboardLayout(kKeyboardLayoutWithAllValidKeys);
  
    auto map = test_keyboard_layout_map.Generate();
    ASSERT_EQ(kKeyboardLayoutWithAllValidKeysCount, map.size());
  }
  
  TEST(DomKeyboardLayoutMapTest, MapGenerationWithTwoValidLayouts) {
    TestDomKeyboardLayoutMap test_keyboard_layout_map_1;
    test_keyboard_layout_map_1.AddKeyboardLayout(kKeyboardLayoutWithAllValidKeys);
    test_keyboard_layout_map_1.AddKeyboardLayout(
        kKeyboardLayoutWithAllValidAlphaKeys);
  
    auto map_1 = test_keyboard_layout_map_1.Generate();
    EXPECT_EQ(kKeyboardLayoutWithAllValidKeysCount, map_1.size());
  
    TestDomKeyboardLayoutMap test_keyboard_layout_map_2;
    test_keyboard_layout_map_2.AddKeyboardLayout(
        kKeyboardLayoutWithAllValidAlphaKeys);
    test_keyboard_layout_map_2.AddKeyboardLayout(kKeyboardLayoutWithAllValidKeys);
  
    auto map_2 = test_keyboard_layout_map_2.Generate();
    EXPECT_EQ(kKeyboardLayoutWithAllValidAlphaKeysCount, map_2.size());
  }
  
  }  // namespace ui
  