// Copyright 2013 The Flutter 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 "flutter/shell/platform/linux/fl_keyboard_manager.h"

#include <cstring>
#include <vector>

#include "flutter/shell/platform/embedder/test_utils/key_codes.g.h"
#include "flutter/shell/platform/linux/fl_binary_messenger_private.h"
#include "flutter/shell/platform/linux/fl_method_codec_private.h"
#include "flutter/shell/platform/linux/key_mapping.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_json_message_codec.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_codec.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h"
#include "flutter/shell/platform/linux/testing/fl_test.h"
#include "flutter/shell/platform/linux/testing/mock_binary_messenger.h"
#include "flutter/shell/platform/linux/testing/mock_text_input_plugin.h"
#include "flutter/testing/testing.h"

#include "gmock/gmock.h"
#include "gtest/gtest.h"

// Define compound `expect` in macros. If they were defined in functions, the
// stacktrace wouldn't print where the function is called in the unit tests.

#define EXPECT_KEY_EVENT(RECORD, TYPE, PHYSICAL, LOGICAL, CHAR, SYNTHESIZED) \
  EXPECT_EQ((RECORD).type, CallRecord::kKeyCallEmbedder);                    \
  EXPECT_EQ((RECORD).event->type, (TYPE));                                   \
  EXPECT_EQ((RECORD).event->physical, (PHYSICAL));                           \
  EXPECT_EQ((RECORD).event->logical, (LOGICAL));                             \
  EXPECT_STREQ((RECORD).event->character, (CHAR));                           \
  EXPECT_EQ((RECORD).event->synthesized, (SYNTHESIZED));

#define VERIFY_DOWN(OUT_LOGICAL, OUT_CHAR)                          \
  EXPECT_EQ(call_records[0].type, CallRecord::kKeyCallEmbedder);    \
  EXPECT_EQ(call_records[0].event->type, kFlutterKeyEventTypeDown); \
  EXPECT_EQ(call_records[0].event->logical, (OUT_LOGICAL));         \
  EXPECT_STREQ(call_records[0].event->character, (OUT_CHAR));       \
  EXPECT_EQ(call_records[0].event->synthesized, false);             \
  call_records.clear()

namespace {
using ::flutter::testing::keycodes::kLogicalAltLeft;
using ::flutter::testing::keycodes::kLogicalBracketLeft;
using ::flutter::testing::keycodes::kLogicalComma;
using ::flutter::testing::keycodes::kLogicalControlLeft;
using ::flutter::testing::keycodes::kLogicalDigit1;
using ::flutter::testing::keycodes::kLogicalKeyA;
using ::flutter::testing::keycodes::kLogicalKeyB;
using ::flutter::testing::keycodes::kLogicalKeyM;
using ::flutter::testing::keycodes::kLogicalKeyQ;
using ::flutter::testing::keycodes::kLogicalMetaLeft;
using ::flutter::testing::keycodes::kLogicalMinus;
using ::flutter::testing::keycodes::kLogicalParenthesisRight;
using ::flutter::testing::keycodes::kLogicalSemicolon;
using ::flutter::testing::keycodes::kLogicalShiftLeft;
using ::flutter::testing::keycodes::kLogicalUnderscore;

using ::flutter::testing::keycodes::kPhysicalAltLeft;
using ::flutter::testing::keycodes::kPhysicalControlLeft;
using ::flutter::testing::keycodes::kPhysicalKeyA;
using ::flutter::testing::keycodes::kPhysicalKeyB;
using ::flutter::testing::keycodes::kPhysicalMetaLeft;
using ::flutter::testing::keycodes::kPhysicalShiftLeft;

// Hardware key codes.
typedef std::function<void(bool handled)> AsyncKeyCallback;
typedef std::function<void(AsyncKeyCallback callback)> ChannelCallHandler;
typedef std::function<void(const FlutterKeyEvent* event,
                           AsyncKeyCallback callback)>
    EmbedderCallHandler;
typedef std::function<void(std::unique_ptr<FlKeyEvent>)> RedispatchHandler;

// A type that can record all kinds of effects that the keyboard manager
// triggers.
//
// An instance of `CallRecord` might not have all the fields filled.
typedef struct {
  enum {
    kKeyCallEmbedder,
    kKeyCallChannel,
  } type;

  AsyncKeyCallback callback;
  std::unique_ptr<FlutterKeyEvent> event;
  std::unique_ptr<char[]> event_character;
} CallRecord;

// Clone a C-string.
//
// Must be deleted by delete[].
char* cloneString(const char* source) {
  if (source == nullptr) {
    return nullptr;
  }
  size_t charLen = strlen(source);
  char* target = new char[charLen + 1];
  strncpy(target, source, charLen + 1);
  return target;
}

constexpr guint16 kKeyCodeKeyA = 0x26u;
constexpr guint16 kKeyCodeKeyB = 0x38u;
constexpr guint16 kKeyCodeKeyM = 0x3au;
constexpr guint16 kKeyCodeDigit1 = 0x0au;
constexpr guint16 kKeyCodeMinus = 0x14u;
constexpr guint16 kKeyCodeSemicolon = 0x2fu;
constexpr guint16 kKeyCodeKeyLeftBracket = 0x22u;

static constexpr char kKeyEventChannelName[] = "flutter/keyevent";
static constexpr char kKeyboardChannelName[] = "flutter/keyboard";
static constexpr char kGetKeyboardStateMethod[] = "getKeyboardState";
static constexpr uint64_t kMockPhysicalKey = 42;
static constexpr uint64_t kMockLogicalKey = 42;

// All key clues for a keyboard layout.
//
// The index is (keyCode * 2 + hasShift), where each value is the character for
// this key (GTK only supports UTF-16.) Since the maximum keycode of interest
// is 128, it has a total of 256 entries..
typedef std::array<uint32_t, 256> MockGroupLayoutData;
typedef std::vector<const MockGroupLayoutData*> MockLayoutData;

extern const MockLayoutData kLayoutUs;
extern const MockLayoutData kLayoutRussian;
extern const MockLayoutData kLayoutFrench;

G_BEGIN_DECLS

G_DECLARE_FINAL_TYPE(FlMockViewDelegate,
                     fl_mock_view_delegate,
                     FL,
                     MOCK_VIEW_DELEGATE,
                     GObject);

G_DECLARE_FINAL_TYPE(FlMockKeyBinaryMessenger,
                     fl_mock_key_binary_messenger,
                     FL,
                     MOCK_KEY_BINARY_MESSENGER,
                     GObject)

G_END_DECLS

MATCHER_P(MethodSuccessResponse, result, "") {
  g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
  g_autoptr(FlMethodResponse) response =
      fl_method_codec_decode_response(FL_METHOD_CODEC(codec), arg, nullptr);
  fl_method_response_get_result(response, nullptr);
  if (fl_value_equal(fl_method_response_get_result(response, nullptr),
                     result)) {
    return true;
  }
  *result_listener << ::testing::PrintToString(response);
  return false;
}

/***** FlMockKeyBinaryMessenger *****/
/* Mock a binary messenger that only processes messages from the embedding on
 * the key event channel, and does so according to the callback set by
 * fl_mock_key_binary_messenger_set_callback_handler */

struct _FlMockKeyBinaryMessenger {
  GObject parent_instance;

  ChannelCallHandler callback_handler;
};

static void fl_mock_key_binary_messenger_iface_init(
    FlBinaryMessengerInterface* iface);

G_DEFINE_TYPE_WITH_CODE(
    FlMockKeyBinaryMessenger,
    fl_mock_key_binary_messenger,
    G_TYPE_OBJECT,
    G_IMPLEMENT_INTERFACE(fl_binary_messenger_get_type(),
                          fl_mock_key_binary_messenger_iface_init))

static void fl_mock_key_binary_messenger_class_init(
    FlMockKeyBinaryMessengerClass* klass) {}

static void fl_mock_key_binary_messenger_send_on_channel(
    FlBinaryMessenger* messenger,
    const gchar* channel,
    GBytes* message,
    GCancellable* cancellable,
    GAsyncReadyCallback callback,
    gpointer user_data) {
  FlMockKeyBinaryMessenger* self = FL_MOCK_KEY_BINARY_MESSENGER(messenger);

  if (callback != nullptr) {
    EXPECT_STREQ(channel, kKeyEventChannelName);
    self->callback_handler([self, cancellable, callback,
                            user_data](bool handled) {
      g_autoptr(GTask) task =
          g_task_new(self, cancellable, callback, user_data);
      g_autoptr(FlValue) result = fl_value_new_map();
      fl_value_set_string_take(result, "handled", fl_value_new_bool(handled));
      g_autoptr(FlJsonMessageCodec) codec = fl_json_message_codec_new();
      g_autoptr(GError) error = nullptr;
      GBytes* data = fl_message_codec_encode_message(FL_MESSAGE_CODEC(codec),
                                                     result, &error);

      g_task_return_pointer(task, data,
                            reinterpret_cast<GDestroyNotify>(g_bytes_unref));
    });
  }
}

static GBytes* fl_mock_key_binary_messenger_send_on_channel_finish(
    FlBinaryMessenger* messenger,
    GAsyncResult* result,
    GError** error) {
  return static_cast<GBytes*>(g_task_propagate_pointer(G_TASK(result), error));
}

static void fl_mock_key_binary_messenger_iface_init(
    FlBinaryMessengerInterface* iface) {
  iface->set_message_handler_on_channel =
      [](FlBinaryMessenger* messenger, const gchar* channel,
         FlBinaryMessengerMessageHandler handler, gpointer user_data,
         GDestroyNotify destroy_notify) {
        EXPECT_STREQ(channel, kKeyEventChannelName);
        // No need to mock. The key event channel expects no incoming messages
        // from the framework.
      };
  iface->send_response = [](FlBinaryMessenger* messenger,
                            FlBinaryMessengerResponseHandle* response_handle,
                            GBytes* response, GError** error) -> gboolean {
    // The key event channel expects no incoming messages from the framework,
    // hence no responses either.
    g_return_val_if_reached(TRUE);
    return TRUE;
  };
  iface->send_on_channel = fl_mock_key_binary_messenger_send_on_channel;
  iface->send_on_channel_finish =
      fl_mock_key_binary_messenger_send_on_channel_finish;
}

static void fl_mock_key_binary_messenger_init(FlMockKeyBinaryMessenger* self) {}

static FlMockKeyBinaryMessenger* fl_mock_key_binary_messenger_new() {
  FlMockKeyBinaryMessenger* self = FL_MOCK_KEY_BINARY_MESSENGER(
      g_object_new(fl_mock_key_binary_messenger_get_type(), NULL));

  // Added to stop compiler complaining about an unused function.
  FL_IS_MOCK_KEY_BINARY_MESSENGER(self);

  return self;
}

static void fl_mock_key_binary_messenger_set_callback_handler(
    FlMockKeyBinaryMessenger* self,
    ChannelCallHandler handler) {
  self->callback_handler = std::move(handler);
}

/***** FlMockViewDelegate *****/

struct _FlMockViewDelegate {
  GObject parent_instance;

  FlMockKeyBinaryMessenger* messenger;
  EmbedderCallHandler embedder_handler;
  bool text_filter_result;
  RedispatchHandler redispatch_handler;
  KeyboardLayoutNotifier layout_notifier;
  const MockLayoutData* layout_data;
};

static void fl_mock_view_keyboard_delegate_iface_init(
    FlKeyboardViewDelegateInterface* iface);

G_DEFINE_TYPE_WITH_CODE(
    FlMockViewDelegate,
    fl_mock_view_delegate,
    G_TYPE_OBJECT,
    G_IMPLEMENT_INTERFACE(fl_keyboard_view_delegate_get_type(),
                          fl_mock_view_keyboard_delegate_iface_init))

static void fl_mock_view_delegate_init(FlMockViewDelegate* self) {}

static void fl_mock_view_delegate_dispose(GObject* object) {
  FlMockViewDelegate* self = FL_MOCK_VIEW_DELEGATE(object);

  g_clear_object(&self->messenger);

  G_OBJECT_CLASS(fl_mock_view_delegate_parent_class)->dispose(object);
}

static void fl_mock_view_delegate_class_init(FlMockViewDelegateClass* klass) {
  G_OBJECT_CLASS(klass)->dispose = fl_mock_view_delegate_dispose;
}

static FlKeyEvent* fl_key_event_clone_information_only(FlKeyEvent* event);

static void fl_mock_view_keyboard_send_key_event(
    FlKeyboardViewDelegate* view_delegate,
    const FlutterKeyEvent* event,
    FlutterKeyEventCallback callback,
    void* user_data) {
  FlMockViewDelegate* self = FL_MOCK_VIEW_DELEGATE(view_delegate);
  self->embedder_handler(event, [callback, user_data](bool handled) {
    if (callback != nullptr) {
      callback(handled, user_data);
    }
  });
}

static gboolean fl_mock_view_keyboard_text_filter_key_press(
    FlKeyboardViewDelegate* view_delegate,
    FlKeyEvent* event) {
  FlMockViewDelegate* self = FL_MOCK_VIEW_DELEGATE(view_delegate);
  return self->text_filter_result;
}

static FlBinaryMessenger* fl_mock_view_keyboard_get_messenger(
    FlKeyboardViewDelegate* view_delegate) {
  FlMockViewDelegate* self = FL_MOCK_VIEW_DELEGATE(view_delegate);
  return FL_BINARY_MESSENGER(self->messenger);
}

static void fl_mock_view_keyboard_redispatch_event(
    FlKeyboardViewDelegate* view_delegate,
    std::unique_ptr<FlKeyEvent> event) {
  FlMockViewDelegate* self = FL_MOCK_VIEW_DELEGATE(view_delegate);
  if (self->redispatch_handler) {
    self->redispatch_handler(std::move(event));
  }
}

static void fl_mock_view_keyboard_subscribe_to_layout_change(
    FlKeyboardViewDelegate* delegate,
    KeyboardLayoutNotifier notifier) {
  FlMockViewDelegate* self = FL_MOCK_VIEW_DELEGATE(delegate);
  self->layout_notifier = std::move(notifier);
}

static guint fl_mock_view_keyboard_lookup_key(FlKeyboardViewDelegate* delegate,
                                              const GdkKeymapKey* key) {
  FlMockViewDelegate* self = FL_MOCK_VIEW_DELEGATE(delegate);
  guint8 group = static_cast<guint8>(key->group);
  EXPECT_LT(group, self->layout_data->size());
  const MockGroupLayoutData* group_layout = (*self->layout_data)[group];
  EXPECT_TRUE(group_layout != nullptr);
  EXPECT_TRUE(key->level == 0 || key->level == 1);
  bool shift = key->level == 1;
  return (*group_layout)[key->keycode * 2 + shift];
}

static GHashTable* fl_mock_view_keyboard_get_keyboard_state(
    FlKeyboardViewDelegate* view_delegate) {
  GHashTable* result = g_hash_table_new(g_direct_hash, g_direct_equal);
  g_hash_table_insert(result, reinterpret_cast<gpointer>(kMockPhysicalKey),
                      reinterpret_cast<gpointer>(kMockLogicalKey));

  return result;
}

static void fl_mock_view_keyboard_delegate_iface_init(
    FlKeyboardViewDelegateInterface* iface) {
  iface->send_key_event = fl_mock_view_keyboard_send_key_event;
  iface->text_filter_key_press = fl_mock_view_keyboard_text_filter_key_press;
  iface->get_messenger = fl_mock_view_keyboard_get_messenger;
  iface->redispatch_event = fl_mock_view_keyboard_redispatch_event;
  iface->subscribe_to_layout_change =
      fl_mock_view_keyboard_subscribe_to_layout_change;
  iface->lookup_key = fl_mock_view_keyboard_lookup_key;
  iface->get_keyboard_state = fl_mock_view_keyboard_get_keyboard_state;
}

static FlMockViewDelegate* fl_mock_view_delegate_new() {
  FlMockViewDelegate* self = FL_MOCK_VIEW_DELEGATE(
      g_object_new(fl_mock_view_delegate_get_type(), nullptr));

  // Added to stop compiler complaining about an unused function.
  FL_IS_MOCK_VIEW_DELEGATE(self);

  self->messenger = fl_mock_key_binary_messenger_new();

  return self;
}

static void fl_mock_view_set_embedder_handler(FlMockViewDelegate* self,
                                              EmbedderCallHandler handler) {
  self->embedder_handler = std::move(handler);
}

static void fl_mock_view_set_text_filter_result(FlMockViewDelegate* self,
                                                bool result) {
  self->text_filter_result = result;
}

static void fl_mock_view_set_redispatch_handler(FlMockViewDelegate* self,
                                                RedispatchHandler handler) {
  self->redispatch_handler = std::move(handler);
}

static void fl_mock_view_set_layout(FlMockViewDelegate* self,
                                    const MockLayoutData* layout) {
  self->layout_data = layout;
  if (self->layout_notifier != nullptr) {
    self->layout_notifier();
  }
}

/***** End FlMockViewDelegate *****/

// Return a newly allocated #FlKeyEvent that is a clone to the given #event
// but with #origin and #dispose set to 0.
static FlKeyEvent* fl_key_event_clone_information_only(FlKeyEvent* event) {
  FlKeyEvent* new_event = fl_key_event_clone(event);
  new_event->origin = nullptr;
  new_event->dispose_origin = nullptr;
  return new_event;
}

// Create a new #FlKeyEvent with the given information.
//
// The #origin will be another #FlKeyEvent with the exact information,
// so that it can be used to redispatch, and is freed upon disposal.
static FlKeyEvent* fl_key_event_new_by_mock(bool is_press,
                                            guint keyval,
                                            guint16 keycode,
                                            int state,
                                            gboolean is_modifier,
                                            guint8 group = 0) {
  FlKeyEvent* event = g_new(FlKeyEvent, 1);
  event->is_press = is_press;
  event->time = 0;
  event->state = state;
  event->keyval = keyval;
  event->string = nullptr;
  event->group = group;
  event->keycode = keycode;
  FlKeyEvent* origin_event = fl_key_event_clone_information_only(event);
  event->origin = origin_event;
  event->dispose_origin = [](gpointer origin) { g_free(origin); };
  return event;
}

class KeyboardTester {
 public:
  KeyboardTester() {
    ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;

    view_ = fl_mock_view_delegate_new();
    respondToEmbedderCallsWith(false);
    respondToChannelCallsWith(false);
    respondToTextInputWith(false);
    setLayout(kLayoutUs);

    manager_ =
        fl_keyboard_manager_new(messenger, FL_KEYBOARD_VIEW_DELEGATE(view_));
  }

  ~KeyboardTester() {
    g_clear_object(&view_);
    g_clear_object(&manager_);
  }

  FlKeyboardManager* manager() { return manager_; }

  // Block until all GdkMainLoop messages are processed, which is basically
  // used only for channel messages.
  void flushChannelMessages() {
    GMainLoop* loop = g_main_loop_new(nullptr, 0);
    g_idle_add(_flushChannelMessagesCb, loop);
    g_main_loop_run(loop);
  }

  // Dispatch each of the given events, expect their results to be false
  // (unhandled), and clear the event array.
  //
  // Returns the number of events redispatched. If any result is unexpected
  // (handled), return a minus number `-x` instead, where `x` is the index of
  // the first unexpected redispatch.
  int redispatchEventsAndClear(
      std::vector<std::unique_ptr<FlKeyEvent>>& events) {
    size_t event_count = events.size();
    int first_error = -1;
    during_redispatch_ = true;
    for (size_t event_id = 0; event_id < event_count; event_id += 1) {
      bool handled = fl_keyboard_manager_handle_event(
          manager_, events[event_id].release());
      EXPECT_FALSE(handled);
      if (handled) {
        first_error = first_error == -1 ? event_id : first_error;
      }
    }
    during_redispatch_ = false;
    events.clear();
    return first_error < 0 ? event_count : -first_error;
  }

  void respondToEmbedderCallsWith(bool response) {
    fl_mock_view_set_embedder_handler(
        view_, [response, this](const FlutterKeyEvent* event,
                                const AsyncKeyCallback& callback) {
          EXPECT_FALSE(during_redispatch_);
          callback(response);
        });
  }

  void recordEmbedderCallsTo(std::vector<CallRecord>& storage) {
    fl_mock_view_set_embedder_handler(
        view_, [&storage, this](const FlutterKeyEvent* event,
                                AsyncKeyCallback callback) {
          EXPECT_FALSE(during_redispatch_);
          auto new_event = std::make_unique<FlutterKeyEvent>(*event);
          char* new_event_character = cloneString(event->character);
          new_event->character = new_event_character;
          storage.push_back(CallRecord{
              .type = CallRecord::kKeyCallEmbedder,
              .callback = std::move(callback),
              .event = std::move(new_event),
              .event_character = std::unique_ptr<char[]>(new_event_character),
          });
        });
  }

  void respondToEmbedderCallsWithAndRecordsTo(
      bool response,
      std::vector<CallRecord>& storage) {
    fl_mock_view_set_embedder_handler(
        view_, [&storage, response, this](const FlutterKeyEvent* event,
                                          const AsyncKeyCallback& callback) {
          EXPECT_FALSE(during_redispatch_);
          auto new_event = std::make_unique<FlutterKeyEvent>(*event);
          char* new_event_character = cloneString(event->character);
          new_event->character = new_event_character;
          storage.push_back(CallRecord{
              .type = CallRecord::kKeyCallEmbedder,
              .event = std::move(new_event),
              .event_character = std::unique_ptr<char[]>(new_event_character),
          });
          callback(response);
        });
  }

  void respondToChannelCallsWith(bool response) {
    fl_mock_key_binary_messenger_set_callback_handler(
        view_->messenger, [response, this](const AsyncKeyCallback& callback) {
          EXPECT_FALSE(during_redispatch_);
          callback(response);
        });
  }

  void recordChannelCallsTo(std::vector<CallRecord>& storage) {
    fl_mock_key_binary_messenger_set_callback_handler(
        view_->messenger, [&storage, this](AsyncKeyCallback callback) {
          EXPECT_FALSE(during_redispatch_);
          storage.push_back(CallRecord{
              .type = CallRecord::kKeyCallChannel,
              .callback = std::move(callback),
          });
        });
  }

  void respondToTextInputWith(bool response) {
    fl_mock_view_set_text_filter_result(view_, response);
  }

  void recordRedispatchedEventsTo(
      std::vector<std::unique_ptr<FlKeyEvent>>& storage) {
    fl_mock_view_set_redispatch_handler(
        view_, [&storage](std::unique_ptr<FlKeyEvent> key) {
          storage.push_back(std::move(key));
        });
  }

  void setLayout(const MockLayoutData& layout) {
    fl_mock_view_set_layout(view_, &layout);
  }

 private:
  FlMockViewDelegate* view_;
  FlKeyboardManager* manager_;
  bool during_redispatch_ = false;

  static gboolean _flushChannelMessagesCb(gpointer data) {
    g_autoptr(GMainLoop) loop = reinterpret_cast<GMainLoop*>(data);
    g_main_loop_quit(loop);
    return FALSE;
  }
};

// Make sure that the keyboard can be disposed without crashes when there are
// unresolved pending events.
TEST(FlKeyboardManagerTest, DisposeWithUnresolvedPends) {
  KeyboardTester tester;
  std::vector<CallRecord> call_records;

  // Record calls so that they aren't responded.
  tester.recordEmbedderCallsTo(call_records);
  fl_keyboard_manager_handle_event(
      tester.manager(),
      fl_key_event_new_by_mock(true, GDK_KEY_a, kKeyCodeKeyA, 0x0, false));

  tester.respondToEmbedderCallsWith(true);
  fl_keyboard_manager_handle_event(
      tester.manager(),
      fl_key_event_new_by_mock(false, GDK_KEY_a, kKeyCodeKeyA, 0x0, false));

  tester.flushChannelMessages();

  // Passes if the cleanup does not crash.
}

TEST(FlKeyboardManagerTest, SingleDelegateWithAsyncResponds) {
  KeyboardTester tester;
  std::vector<CallRecord> call_records;
  std::vector<std::unique_ptr<FlKeyEvent>> redispatched;

  gboolean manager_handled = false;

  /// Test 1: One event that is handled by the framework
  tester.recordEmbedderCallsTo(call_records);
  tester.recordRedispatchedEventsTo(redispatched);

  // Dispatch a key event
  manager_handled = fl_keyboard_manager_handle_event(
      tester.manager(),
      fl_key_event_new_by_mock(true, GDK_KEY_a, kKeyCodeKeyA, 0x0, false));
  tester.flushChannelMessages();
  EXPECT_EQ(manager_handled, true);
  EXPECT_EQ(redispatched.size(), 0u);
  EXPECT_EQ(call_records.size(), 1u);
  EXPECT_KEY_EVENT(call_records[0], kFlutterKeyEventTypeDown, kPhysicalKeyA,
                   kLogicalKeyA, "a", false);

  call_records[0].callback(true);
  tester.flushChannelMessages();
  EXPECT_EQ(redispatched.size(), 0u);
  EXPECT_TRUE(fl_keyboard_manager_is_state_clear(tester.manager()));
  call_records.clear();

  /// Test 2: Two events that are unhandled by the framework
  manager_handled = fl_keyboard_manager_handle_event(
      tester.manager(),
      fl_key_event_new_by_mock(false, GDK_KEY_a, kKeyCodeKeyA, 0x0, false));
  tester.flushChannelMessages();
  EXPECT_EQ(manager_handled, true);
  EXPECT_EQ(redispatched.size(), 0u);
  EXPECT_EQ(call_records.size(), 1u);
  EXPECT_KEY_EVENT(call_records[0], kFlutterKeyEventTypeUp, kPhysicalKeyA,
                   kLogicalKeyA, nullptr, false);

  // Dispatch another key event
  manager_handled = fl_keyboard_manager_handle_event(
      tester.manager(),
      fl_key_event_new_by_mock(true, GDK_KEY_b, kKeyCodeKeyB, 0x0, false));
  tester.flushChannelMessages();
  EXPECT_EQ(manager_handled, true);
  EXPECT_EQ(redispatched.size(), 0u);
  EXPECT_EQ(call_records.size(), 2u);
  EXPECT_KEY_EVENT(call_records[1], kFlutterKeyEventTypeDown, kPhysicalKeyB,
                   kLogicalKeyB, "b", false);

  // Resolve the second event first to test out-of-order response
  call_records[1].callback(false);
  EXPECT_EQ(redispatched.size(), 1u);
  EXPECT_EQ(redispatched[0]->keyval, 0x62u);
  call_records[0].callback(false);
  tester.flushChannelMessages();
  EXPECT_EQ(redispatched.size(), 2u);
  EXPECT_EQ(redispatched[1]->keyval, 0x61u);

  EXPECT_FALSE(fl_keyboard_manager_is_state_clear(tester.manager()));
  call_records.clear();

  // Resolve redispatches
  EXPECT_EQ(tester.redispatchEventsAndClear(redispatched), 2);
  tester.flushChannelMessages();
  EXPECT_EQ(call_records.size(), 0u);
  EXPECT_TRUE(fl_keyboard_manager_is_state_clear(tester.manager()));

  /// Test 3: Dispatch the same event again to ensure that prevention from
  /// redispatching only works once.
  manager_handled = fl_keyboard_manager_handle_event(
      tester.manager(),
      fl_key_event_new_by_mock(false, GDK_KEY_a, kKeyCodeKeyA, 0x0, false));
  tester.flushChannelMessages();
  EXPECT_EQ(manager_handled, true);
  EXPECT_EQ(redispatched.size(), 0u);
  EXPECT_EQ(call_records.size(), 1u);

  call_records[0].callback(true);
  EXPECT_TRUE(fl_keyboard_manager_is_state_clear(tester.manager()));
}

TEST(FlKeyboardManagerTest, SingleDelegateWithSyncResponds) {
  KeyboardTester tester;
  gboolean manager_handled = false;
  std::vector<CallRecord> call_records;
  std::vector<std::unique_ptr<FlKeyEvent>> redispatched;

  /// Test 1: One event that is handled by the framework
  tester.respondToEmbedderCallsWithAndRecordsTo(true, call_records);
  tester.recordRedispatchedEventsTo(redispatched);

  // Dispatch a key event
  manager_handled = fl_keyboard_manager_handle_event(
      tester.manager(),
      fl_key_event_new_by_mock(true, GDK_KEY_a, kKeyCodeKeyA, 0x0, false));
  tester.flushChannelMessages();
  EXPECT_EQ(manager_handled, true);
  EXPECT_EQ(call_records.size(), 1u);
  EXPECT_KEY_EVENT(call_records[0], kFlutterKeyEventTypeDown, kPhysicalKeyA,
                   kLogicalKeyA, "a", false);
  EXPECT_EQ(redispatched.size(), 0u);
  call_records.clear();

  EXPECT_TRUE(fl_keyboard_manager_is_state_clear(tester.manager()));
  redispatched.clear();

  /// Test 2: An event unhandled by the framework
  tester.respondToEmbedderCallsWithAndRecordsTo(false, call_records);
  manager_handled = fl_keyboard_manager_handle_event(
      tester.manager(),
      fl_key_event_new_by_mock(false, GDK_KEY_a, kKeyCodeKeyA, 0x0, false));
  tester.flushChannelMessages();
  EXPECT_EQ(manager_handled, true);
  EXPECT_EQ(call_records.size(), 1u);
  EXPECT_KEY_EVENT(call_records[0], kFlutterKeyEventTypeUp, kPhysicalKeyA,
                   kLogicalKeyA, nullptr, false);
  EXPECT_EQ(redispatched.size(), 1u);
  call_records.clear();

  EXPECT_FALSE(fl_keyboard_manager_is_state_clear(tester.manager()));

  EXPECT_EQ(tester.redispatchEventsAndClear(redispatched), 1);
  EXPECT_EQ(call_records.size(), 0u);

  EXPECT_TRUE(fl_keyboard_manager_is_state_clear(tester.manager()));
}

TEST(FlKeyboardManagerTest, WithTwoAsyncDelegates) {
  KeyboardTester tester;
  std::vector<CallRecord> call_records;
  std::vector<std::unique_ptr<FlKeyEvent>> redispatched;

  gboolean manager_handled = false;

  tester.recordEmbedderCallsTo(call_records);
  tester.recordChannelCallsTo(call_records);
  tester.recordRedispatchedEventsTo(redispatched);

  /// Test 1: One delegate responds true, the other false

  manager_handled = fl_keyboard_manager_handle_event(
      tester.manager(),
      fl_key_event_new_by_mock(true, GDK_KEY_a, kKeyCodeKeyA, 0x0, false));

  EXPECT_EQ(manager_handled, true);
  EXPECT_EQ(redispatched.size(), 0u);
  EXPECT_EQ(call_records.size(), 2u);

  EXPECT_EQ(call_records[0].type, CallRecord::kKeyCallEmbedder);
  EXPECT_EQ(call_records[1].type, CallRecord::kKeyCallChannel);

  call_records[0].callback(true);
  call_records[1].callback(false);
  tester.flushChannelMessages();
  EXPECT_EQ(redispatched.size(), 0u);

  EXPECT_TRUE(fl_keyboard_manager_is_state_clear(tester.manager()));
  call_records.clear();

  /// Test 2: All delegates respond false
  manager_handled = fl_keyboard_manager_handle_event(
      tester.manager(),
      fl_key_event_new_by_mock(false, GDK_KEY_a, kKeyCodeKeyA, 0x0, false));

  EXPECT_EQ(manager_handled, true);
  EXPECT_EQ(redispatched.size(), 0u);
  EXPECT_EQ(call_records.size(), 2u);

  EXPECT_EQ(call_records[0].type, CallRecord::kKeyCallEmbedder);
  EXPECT_EQ(call_records[1].type, CallRecord::kKeyCallChannel);

  call_records[0].callback(false);
  call_records[1].callback(false);

  call_records.clear();

  // Resolve redispatch
  tester.flushChannelMessages();
  EXPECT_EQ(redispatched.size(), 1u);
  EXPECT_EQ(tester.redispatchEventsAndClear(redispatched), 1);
  EXPECT_EQ(call_records.size(), 0u);

  EXPECT_TRUE(fl_keyboard_manager_is_state_clear(tester.manager()));
}

TEST(FlKeyboardManagerTest, TextInputPluginReturnsFalse) {
  KeyboardTester tester;
  std::vector<std::unique_ptr<FlKeyEvent>> redispatched;
  gboolean manager_handled = false;
  tester.recordRedispatchedEventsTo(redispatched);
  tester.respondToTextInputWith(false);

  // Dispatch a key event.
  manager_handled = fl_keyboard_manager_handle_event(
      tester.manager(),
      fl_key_event_new_by_mock(true, GDK_KEY_a, kKeyCodeKeyA, 0, false));
  tester.flushChannelMessages();
  EXPECT_EQ(manager_handled, true);
  // The event was redispatched because no one handles it.
  EXPECT_EQ(redispatched.size(), 1u);

  // Resolve redispatched event.
  EXPECT_EQ(tester.redispatchEventsAndClear(redispatched), 1);

  EXPECT_TRUE(fl_keyboard_manager_is_state_clear(tester.manager()));
}

TEST(FlKeyboardManagerTest, TextInputPluginReturnsTrue) {
  KeyboardTester tester;
  std::vector<std::unique_ptr<FlKeyEvent>> redispatched;
  gboolean manager_handled = false;
  tester.recordRedispatchedEventsTo(redispatched);
  tester.respondToTextInputWith(true);

  // Dispatch a key event.
  manager_handled = fl_keyboard_manager_handle_event(
      tester.manager(),
      fl_key_event_new_by_mock(true, GDK_KEY_a, kKeyCodeKeyA, 0, false));
  tester.flushChannelMessages();
  EXPECT_EQ(manager_handled, true);
  // The event was not redispatched because text input plugin handles it.
  EXPECT_EQ(redispatched.size(), 0u);

  EXPECT_TRUE(fl_keyboard_manager_is_state_clear(tester.manager()));
}

TEST(FlKeyboardManagerTest, CorrectLogicalKeyForLayouts) {
  KeyboardTester tester;

  std::vector<CallRecord> call_records;
  tester.recordEmbedderCallsTo(call_records);

  auto sendTap = [&](guint8 keycode, guint keyval, guint8 group) {
    fl_keyboard_manager_handle_event(
        tester.manager(),
        fl_key_event_new_by_mock(true, keyval, keycode, 0, false, group));
    fl_keyboard_manager_handle_event(
        tester.manager(),
        fl_key_event_new_by_mock(false, keyval, keycode, 0, false, group));
  };

  /* US keyboard layout */

  sendTap(kKeyCodeKeyA, GDK_KEY_a, 0);  // KeyA
  VERIFY_DOWN(kLogicalKeyA, "a");

  sendTap(kKeyCodeKeyA, GDK_KEY_A, 0);  // Shift-KeyA
  VERIFY_DOWN(kLogicalKeyA, "A");

  sendTap(kKeyCodeDigit1, GDK_KEY_1, 0);  // Digit1
  VERIFY_DOWN(kLogicalDigit1, "1");

  sendTap(kKeyCodeDigit1, GDK_KEY_exclam, 0);  // Shift-Digit1
  VERIFY_DOWN(kLogicalDigit1, "!");

  sendTap(kKeyCodeMinus, GDK_KEY_minus, 0);  // Minus
  VERIFY_DOWN(kLogicalMinus, "-");

  sendTap(kKeyCodeMinus, GDK_KEY_underscore, 0);  // Shift-Minus
  VERIFY_DOWN(kLogicalUnderscore, "_");

  /* French keyboard layout, group 3, which is when the input method is showing
   * "Fr" */

  tester.setLayout(kLayoutFrench);

  sendTap(kKeyCodeKeyA, GDK_KEY_q, 3);  // KeyA
  VERIFY_DOWN(kLogicalKeyQ, "q");

  sendTap(kKeyCodeKeyA, GDK_KEY_Q, 3);  // Shift-KeyA
  VERIFY_DOWN(kLogicalKeyQ, "Q");

  sendTap(kKeyCodeSemicolon, GDK_KEY_m, 3);  // ; but prints M
  VERIFY_DOWN(kLogicalKeyM, "m");

  sendTap(kKeyCodeKeyM, GDK_KEY_comma, 3);  // M but prints ,
  VERIFY_DOWN(kLogicalComma, ",");

  sendTap(kKeyCodeDigit1, GDK_KEY_ampersand, 3);  // Digit1
  VERIFY_DOWN(kLogicalDigit1, "&");

  sendTap(kKeyCodeDigit1, GDK_KEY_1, 3);  // Shift-Digit1
  VERIFY_DOWN(kLogicalDigit1, "1");

  sendTap(kKeyCodeMinus, GDK_KEY_parenright, 3);  // Minus
  VERIFY_DOWN(kLogicalParenthesisRight, ")");

  sendTap(kKeyCodeMinus, GDK_KEY_degree, 3);  // Shift-Minus
  VERIFY_DOWN(static_cast<uint32_t>(L'°'), "°");

  /* French keyboard layout, group 0, which is pressing the "extra key for
   * triggering input method" key once after switching to French IME. */

  sendTap(kKeyCodeKeyA, GDK_KEY_a, 0);  // KeyA
  VERIFY_DOWN(kLogicalKeyA, "a");

  sendTap(kKeyCodeDigit1, GDK_KEY_1, 0);  // Digit1
  VERIFY_DOWN(kLogicalDigit1, "1");

  /* Russian keyboard layout, group 2 */
  tester.setLayout(kLayoutRussian);

  sendTap(kKeyCodeKeyA, GDK_KEY_Cyrillic_ef, 2);  // KeyA
  VERIFY_DOWN(kLogicalKeyA, "ф");

  sendTap(kKeyCodeDigit1, GDK_KEY_1, 2);  // Shift-Digit1
  VERIFY_DOWN(kLogicalDigit1, "1");

  sendTap(kKeyCodeKeyLeftBracket, GDK_KEY_Cyrillic_ha, 2);
  VERIFY_DOWN(kLogicalBracketLeft, "х");

  /* Russian keyboard layout, group 0 */
  sendTap(kKeyCodeKeyA, GDK_KEY_a, 0);  // KeyA
  VERIFY_DOWN(kLogicalKeyA, "a");

  sendTap(kKeyCodeKeyLeftBracket, GDK_KEY_bracketleft, 0);
  VERIFY_DOWN(kLogicalBracketLeft, "[");
}

TEST(FlKeyboardManagerTest, SynthesizeModifiersIfNeeded) {
  KeyboardTester tester;
  std::vector<CallRecord> call_records;
  tester.recordEmbedderCallsTo(call_records);

  auto verifyModifierIsSynthesized = [&](GdkModifierType mask,
                                         uint64_t physical, uint64_t logical) {
    // Modifier is pressed.
    guint state = mask;
    fl_keyboard_manager_sync_modifier_if_needed(tester.manager(), state, 1000);
    EXPECT_EQ(call_records.size(), 1u);
    EXPECT_KEY_EVENT(call_records[0], kFlutterKeyEventTypeDown, physical,
                     logical, NULL, true);
    // Modifier is released.
    state = state ^ mask;
    fl_keyboard_manager_sync_modifier_if_needed(tester.manager(), state, 1001);
    EXPECT_EQ(call_records.size(), 2u);
    EXPECT_KEY_EVENT(call_records[1], kFlutterKeyEventTypeUp, physical, logical,
                     NULL, true);
    call_records.clear();
  };

  // No modifiers pressed.
  guint state = 0;
  fl_keyboard_manager_sync_modifier_if_needed(tester.manager(), state, 1000);
  EXPECT_EQ(call_records.size(), 0u);
  call_records.clear();

  // Press and release each modifier once.
  verifyModifierIsSynthesized(GDK_CONTROL_MASK, kPhysicalControlLeft,
                              kLogicalControlLeft);
  verifyModifierIsSynthesized(GDK_META_MASK, kPhysicalMetaLeft,
                              kLogicalMetaLeft);
  verifyModifierIsSynthesized(GDK_MOD1_MASK, kPhysicalAltLeft, kLogicalAltLeft);
  verifyModifierIsSynthesized(GDK_SHIFT_MASK, kPhysicalShiftLeft,
                              kLogicalShiftLeft);
}

TEST(FlKeyboardManagerTest, GetPressedState) {
  KeyboardTester tester;
  tester.respondToTextInputWith(true);

  // Dispatch a key event.
  fl_keyboard_manager_handle_event(
      tester.manager(),
      fl_key_event_new_by_mock(true, GDK_KEY_a, kKeyCodeKeyA, 0, false));

  GHashTable* pressedState =
      fl_keyboard_manager_get_pressed_state(tester.manager());
  EXPECT_EQ(g_hash_table_size(pressedState), 1u);

  gpointer physical_key =
      g_hash_table_lookup(pressedState, uint64_to_gpointer(kPhysicalKeyA));
  EXPECT_EQ(gpointer_to_uint64(physical_key), kLogicalKeyA);
}

TEST(FlKeyboardPluginTest, KeyboardChannelGetPressedState) {
  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;

  g_autoptr(FlKeyboardManager) manager = fl_keyboard_manager_new(
      messenger, FL_KEYBOARD_VIEW_DELEGATE(fl_mock_view_delegate_new()));
  EXPECT_NE(manager, nullptr);

  g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
  g_autoptr(GBytes) message = fl_method_codec_encode_method_call(
      FL_METHOD_CODEC(codec), kGetKeyboardStateMethod, nullptr, nullptr);

  g_autoptr(FlValue) response = fl_value_new_map();
  fl_value_set_take(response, fl_value_new_int(kMockPhysicalKey),
                    fl_value_new_int(kMockLogicalKey));
  EXPECT_CALL(messenger,
              fl_binary_messenger_send_response(
                  ::testing::Eq<FlBinaryMessenger*>(messenger), ::testing::_,
                  MethodSuccessResponse(response), ::testing::_))
      .WillOnce(::testing::Return(true));

  messenger.ReceiveMessage(kKeyboardChannelName, message);
}

// The following layout data is generated using DEBUG_PRINT_LAYOUT.

const MockGroupLayoutData kLayoutUs0{{
    // +0x0  Shift   +0x1    Shift   +0x2    Shift   +0x3    Shift
    0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,  // 0x00
    0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,  // 0x04
    0xffff, 0x0031, 0xffff, 0x0031, 0x0031, 0x0021, 0x0032, 0x0040,  // 0x08
    0x0033, 0x0023, 0x0034, 0x0024, 0x0035, 0x0025, 0x0036, 0x005e,  // 0x0c
    0x0037, 0x0026, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029,  // 0x10
    0x002d, 0x005f, 0x003d, 0x002b, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x14
    0x0071, 0x0051, 0x0077, 0x0057, 0x0065, 0x0045, 0x0072, 0x0052,  // 0x18
    0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049,  // 0x1c
    0x006f, 0x004f, 0x0070, 0x0050, 0x005b, 0x007b, 0x005d, 0x007d,  // 0x20
    0xffff, 0xffff, 0xffff, 0x0061, 0x0061, 0x0041, 0x0073, 0x0053,  // 0x24
    0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048,  // 0x28
    0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x003b, 0x003a,  // 0x2c
    0x0027, 0x0022, 0x0060, 0x007e, 0xffff, 0x005c, 0x005c, 0x007c,  // 0x30
    0x007a, 0x005a, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056,  // 0x34
    0x0062, 0x0042, 0x006e, 0x004e, 0x006d, 0x004d, 0x002c, 0x003c,  // 0x38
    0x002e, 0x003e, 0x002f, 0x003f, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x3c
    0xffff, 0xffff, 0x0020, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x40
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x44
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x48
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x4c
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x50
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x54
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x58
    0xffff, 0xffff, 0x003c, 0x003e, 0x003c, 0x003e, 0xffff, 0xffff,  // 0x5c
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x60
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x64
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x68
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x6c
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x70
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x74
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x78
    0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff, 0xffff, 0xffff,  // 0x7c
}};

const MockGroupLayoutData kLayoutRussian0{
    // +0x0  Shift   +0x1    Shift   +0x2    Shift   +0x3    Shift
    0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,  // 0x00
    0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,  // 0x04
    0x0000, 0xffff, 0xffff, 0x0031, 0x0031, 0x0021, 0x0032, 0x0040,  // 0x08
    0x0033, 0x0023, 0x0034, 0x0024, 0x0035, 0x0025, 0x0036, 0x005e,  // 0x0c
    0x0037, 0x0026, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029,  // 0x10
    0x002d, 0x005f, 0x003d, 0x002b, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x14
    0x0071, 0x0051, 0x0077, 0x0057, 0x0065, 0x0045, 0x0072, 0x0052,  // 0x18
    0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049,  // 0x1c
    0x006f, 0x004f, 0x0070, 0x0050, 0x005b, 0x007b, 0x005d, 0x007d,  // 0x20
    0xffff, 0xffff, 0xffff, 0x0061, 0x0061, 0x0041, 0x0073, 0x0053,  // 0x24
    0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048,  // 0x28
    0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x003b, 0x003a,  // 0x2c
    0x0027, 0x0022, 0x0060, 0x007e, 0xffff, 0x005c, 0x005c, 0x007c,  // 0x30
    0x007a, 0x005a, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056,  // 0x34
    0x0062, 0x0042, 0x006e, 0x004e, 0x006d, 0x004d, 0x002c, 0x003c,  // 0x38
    0x002e, 0x003e, 0x002f, 0x003f, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x3c
    0xffff, 0xffff, 0x0020, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x40
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x44
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x48
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x4c
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x50
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x54
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x58
    0xffff, 0xffff, 0x0000, 0xffff, 0x003c, 0x003e, 0xffff, 0xffff,  // 0x5c
    0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x60
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff,  // 0x64
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x68
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x6c
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x70
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x74
    0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x78
    0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff, 0xffff, 0xffff,  // 0x7c
};

const MockGroupLayoutData kLayoutRussian2{{
    // +0x0  Shift   +0x1    Shift   +0x2    Shift   +0x3    Shift
    0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,  // 0x00
    0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,  // 0x04
    0xffff, 0x0031, 0x0021, 0x0000, 0x0031, 0x0021, 0x0032, 0x0022,  // 0x08
    0x0033, 0x06b0, 0x0034, 0x003b, 0x0035, 0x0025, 0x0036, 0x003a,  // 0x0c
    0x0037, 0x003f, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029,  // 0x10
    0x002d, 0x005f, 0x003d, 0x002b, 0x0071, 0x0051, 0x0000, 0x0000,  // 0x14
    0x06ca, 0x06ea, 0x06c3, 0x06e3, 0x06d5, 0x06f5, 0x06cb, 0x06eb,  // 0x18
    0x06c5, 0x06e5, 0x06ce, 0x06ee, 0x06c7, 0x06e7, 0x06db, 0x06fb,  // 0x1c
    0x06dd, 0x06fd, 0x06da, 0x06fa, 0x06c8, 0x06e8, 0x06df, 0x06ff,  // 0x20
    0x0061, 0x0041, 0x0041, 0x0000, 0x06c6, 0x06e6, 0x06d9, 0x06f9,  // 0x24
    0x06d7, 0x06f7, 0x06c1, 0x06e1, 0x06d0, 0x06f0, 0x06d2, 0x06f2,  // 0x28
    0x06cf, 0x06ef, 0x06cc, 0x06ec, 0x06c4, 0x06e4, 0x06d6, 0x06f6,  // 0x2c
    0x06dc, 0x06fc, 0x06a3, 0x06b3, 0x007c, 0x0000, 0x005c, 0x002f,  // 0x30
    0x06d1, 0x06f1, 0x06de, 0x06fe, 0x06d3, 0x06f3, 0x06cd, 0x06ed,  // 0x34
    0x06c9, 0x06e9, 0x06d4, 0x06f4, 0x06d8, 0x06f8, 0x06c2, 0x06e2,  // 0x38
    0x06c0, 0x06e0, 0x002e, 0x002c, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x3c
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x40
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x44
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x48
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x4c
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x50
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x54
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x58
    0xffff, 0xffff, 0x003c, 0x003e, 0x002f, 0x007c, 0xffff, 0xffff,  // 0x5c
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x60
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x64
    0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0x0000,  // 0x68
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x6c
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x70
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x74
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x00b1,  // 0x78
    0x00b1, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x7c
}};

const MockGroupLayoutData kLayoutFrench0 = {
    // +0x0  Shift   +0x1    Shift   +0x2    Shift   +0x3    Shift
    0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,  // 0x00
    0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,  // 0x04
    0x0000, 0xffff, 0xffff, 0x0031, 0x0031, 0x0021, 0x0032, 0x0040,  // 0x08
    0x0033, 0x0023, 0x0034, 0x0024, 0x0035, 0x0025, 0x0036, 0x005e,  // 0x0c
    0x0037, 0x0026, 0x0038, 0x002a, 0x0039, 0x0028, 0x0030, 0x0029,  // 0x10
    0x002d, 0x005f, 0x003d, 0x002b, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x14
    0x0071, 0x0051, 0x0077, 0x0057, 0x0065, 0x0045, 0x0072, 0x0052,  // 0x18
    0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049,  // 0x1c
    0x006f, 0x004f, 0x0070, 0x0050, 0x005b, 0x007b, 0x005d, 0x007d,  // 0x20
    0xffff, 0xffff, 0xffff, 0x0061, 0x0061, 0x0041, 0x0073, 0x0053,  // 0x24
    0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048,  // 0x28
    0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x003b, 0x003a,  // 0x2c
    0x0027, 0x0022, 0x0060, 0x007e, 0xffff, 0x005c, 0x005c, 0x007c,  // 0x30
    0x007a, 0x005a, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056,  // 0x34
    0x0062, 0x0042, 0x006e, 0x004e, 0x006d, 0x004d, 0x002c, 0x003c,  // 0x38
    0x002e, 0x003e, 0x002f, 0x003f, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x3c
    0xffff, 0xffff, 0x0020, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x40
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x44
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x48
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x4c
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x50
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x54
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x58
    0xffff, 0xffff, 0x0000, 0xffff, 0x003c, 0x003e, 0xffff, 0xffff,  // 0x5c
    0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x60
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff,  // 0x64
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x68
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x6c
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x70
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x74
    0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x78
    0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff, 0xffff, 0xffff,  // 0x7c
};

const MockGroupLayoutData kLayoutFrench3 = {
    // +0x0  Shift   +0x1    Shift   +0x2    Shift   +0x3    Shift
    0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,  // 0x00
    0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,  // 0x04
    0x0000, 0xffff, 0x0000, 0x0000, 0x0026, 0x0031, 0x00e9, 0x0032,  // 0x08
    0x0022, 0x0033, 0x0027, 0x0034, 0x0028, 0x0035, 0x002d, 0x0036,  // 0x0c
    0x00e8, 0x0037, 0x005f, 0x0038, 0x00e7, 0x0039, 0x00e0, 0x0030,  // 0x10
    0x0029, 0x00b0, 0x003d, 0x002b, 0x0000, 0x0000, 0x0061, 0x0041,  // 0x14
    0x0061, 0x0041, 0x007a, 0x005a, 0x0065, 0x0045, 0x0072, 0x0052,  // 0x18
    0x0074, 0x0054, 0x0079, 0x0059, 0x0075, 0x0055, 0x0069, 0x0049,  // 0x1c
    0x006f, 0x004f, 0x0070, 0x0050, 0xffff, 0xffff, 0x0024, 0x00a3,  // 0x20
    0x0041, 0x0000, 0x0000, 0x0000, 0x0071, 0x0051, 0x0073, 0x0053,  // 0x24
    0x0064, 0x0044, 0x0066, 0x0046, 0x0067, 0x0047, 0x0068, 0x0048,  // 0x28
    0x006a, 0x004a, 0x006b, 0x004b, 0x006c, 0x004c, 0x006d, 0x004d,  // 0x2c
    0x00f9, 0x0025, 0x00b2, 0x007e, 0x0000, 0x0000, 0x002a, 0x00b5,  // 0x30
    0x0077, 0x0057, 0x0078, 0x0058, 0x0063, 0x0043, 0x0076, 0x0056,  // 0x34
    0x0062, 0x0042, 0x006e, 0x004e, 0x002c, 0x003f, 0x003b, 0x002e,  // 0x38
    0x003a, 0x002f, 0x0021, 0x00a7, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x3c
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x40
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x44
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x48
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x4c
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x50
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x54
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x58
    0xffff, 0x003c, 0x0000, 0xffff, 0x003c, 0x003e, 0xffff, 0xffff,  // 0x5c
    0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x60
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0xffff,  // 0x64
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x68
    0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x6c
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x70
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x74
    0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0x00b1, 0x00b1, 0xffff,  // 0x78
    0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,  // 0x7c
};

const MockLayoutData kLayoutUs{&kLayoutUs0};
const MockLayoutData kLayoutRussian{&kLayoutRussian0, nullptr,
                                    &kLayoutRussian2};
const MockLayoutData kLayoutFrench{&kLayoutFrench0, nullptr, nullptr,
                                   &kLayoutFrench3};

}  // namespace
