chromium/ui/events/win/modifier_keyboard_hook_win_unittest.cc

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

#include <memory>
#include <optional>
#include <vector>

#include "base/containers/flat_set.h"
#include "base/functional/bind.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/event.h"
#include "ui/events/keyboard_hook.h"
#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/events/keycodes/dom/keycode_converter.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/events/test/keyboard_layout.h"
#include "ui/events/win/keyboard_hook_win_base.h"
#include "ui/events/win/system_event_state_lookup.h"

namespace ui {

class ModifierKeyboardHookWinTest : public testing::Test {
 public:
  ModifierKeyboardHookWinTest();

  ModifierKeyboardHookWinTest(const ModifierKeyboardHookWinTest&) = delete;
  ModifierKeyboardHookWinTest& operator=(const ModifierKeyboardHookWinTest&) =
      delete;

  ~ModifierKeyboardHookWinTest() override;

  // testing::Test overrides.
  void SetUp() override;

  void HandleKeyPress(KeyEvent* key_event);

 protected:
  KeyboardHookWinBase* keyboard_hook() { return keyboard_hook_.get(); }

  uint32_t next_time_stamp() { return time_stamp_++; }

  std::vector<KeyEvent>* key_events() { return &key_events_; }

  // Used for sending key events which are handled by the hook.
  void SendModifierKeyDownEvent(KeyboardCode key_code,
                                DomCode dom_code,
                                int repeat_count = 1);
  void SendModifierKeyUpEvent(KeyboardCode key_code, DomCode dom_code);

  void SetKeyboardLayoutForTest(KeyboardLayout new_layout);

 private:
  uint32_t time_stamp_ = 0;
  std::unique_ptr<KeyboardHookWinBase> keyboard_hook_;
  std::vector<KeyEvent> key_events_;
  std::unique_ptr<ScopedKeyboardLayout> keyboard_layout_;
};

ModifierKeyboardHookWinTest::ModifierKeyboardHookWinTest() = default;

ModifierKeyboardHookWinTest::~ModifierKeyboardHookWinTest() = default;

void ModifierKeyboardHookWinTest::SetUp() {
  keyboard_hook_ = KeyboardHookWinBase::CreateModifierKeyboardHookForTesting(
      std::optional<base::flat_set<DomCode>>(),
      base::BindRepeating(&ModifierKeyboardHookWinTest::HandleKeyPress,
                          base::Unretained(this)));

  keyboard_layout_ = std::make_unique<ScopedKeyboardLayout>(
      KeyboardLayout::KEYBOARD_LAYOUT_ENGLISH_US);
}

void ModifierKeyboardHookWinTest::HandleKeyPress(KeyEvent* key_event) {
  key_events_.push_back(*key_event);
}

void ModifierKeyboardHookWinTest::SendModifierKeyDownEvent(
    KeyboardCode key_code,
    DomCode dom_code,
    int repeat_count /*=1*/) {
  // Ensure we have a valid repeat count and the modifer passed in contains
  // location information.
  DCHECK_GT(repeat_count, 0);
  DCHECK_NE(key_code, KeyboardCode::VKEY_CONTROL);
  DCHECK_NE(key_code, KeyboardCode::VKEY_MENU);

  for (int i = 0; i < repeat_count; i++) {
    ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage(
        WM_KEYDOWN, key_code,
        KeycodeConverter::DomCodeToNativeKeycode(dom_code), next_time_stamp()));
  }
}

void ModifierKeyboardHookWinTest::SendModifierKeyUpEvent(KeyboardCode key_code,
                                                         DomCode dom_code) {
  // Ensure we have a valid repeat count and the modifer passed in contains
  // location information.
  DCHECK_NE(key_code, KeyboardCode::VKEY_CONTROL);
  DCHECK_NE(key_code, KeyboardCode::VKEY_MENU);

  ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage(
      WM_KEYUP, key_code, KeycodeConverter::DomCodeToNativeKeycode(dom_code),
      next_time_stamp()));
}

namespace {
void VerifyKeyEvent(KeyEvent* key_event,
                    KeyboardCode non_located_key_code,
                    DomCode dom_code,
                    bool key_down,
                    bool is_repeat) {
  if (key_down) {
    ASSERT_EQ(key_event->type(), EventType::kKeyPressed);
    ASSERT_EQ(key_event->is_repeat(), is_repeat);
  } else {
    ASSERT_EQ(key_event->type(), EventType::kKeyReleased);
    ASSERT_FALSE(key_event->is_repeat());
  }
  ASSERT_EQ(key_event->key_code(), non_located_key_code);
  ASSERT_EQ(key_event->code(), dom_code);
}
}  // namespace

TEST_F(ModifierKeyboardHookWinTest, SimpleLeftControlKeypressTest) {
  const KeyboardCode key_code = KeyboardCode::VKEY_LCONTROL;
  const DomCode dom_code = DomCode::CONTROL_LEFT;
  SendModifierKeyDownEvent(key_code, dom_code);
  ASSERT_EQ(key_events()->size(), 1ULL);
  SendModifierKeyUpEvent(key_code, dom_code);
  ASSERT_EQ(key_events()->size(), 2ULL);

  KeyEvent down_event = key_events()->at(0);
  VerifyKeyEvent(&down_event, KeyboardCode::VKEY_CONTROL, dom_code, true,
                 false);
  ASSERT_TRUE(down_event.IsControlDown());
  ASSERT_FALSE(down_event.IsAltDown());
  ASSERT_FALSE(down_event.IsCommandDown());

  KeyEvent up_event = key_events()->at(1);
  VerifyKeyEvent(&up_event, KeyboardCode::VKEY_CONTROL, dom_code, false, false);
  ASSERT_FALSE(up_event.IsControlDown());
}

TEST_F(ModifierKeyboardHookWinTest, RepeatingLeftControlKeypressTest) {
  const int repeat_count = 10;
  const KeyboardCode key_code = KeyboardCode::VKEY_LCONTROL;
  const DomCode dom_code = DomCode::CONTROL_LEFT;
  SendModifierKeyDownEvent(key_code, dom_code, repeat_count);
  ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count);
  SendModifierKeyUpEvent(key_code, dom_code);
  ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count + 1);

  bool should_repeat = false;
  for (int i = 0; i < repeat_count; i++) {
    KeyEvent event = key_events()->at(i);
    VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, dom_code, true,
                   should_repeat);
    ASSERT_TRUE(event.IsControlDown());
    ASSERT_FALSE(event.IsAltDown());
    ASSERT_FALSE(event.IsCommandDown());
    should_repeat = true;
  }

  KeyEvent up_event = key_events()->at(repeat_count);
  VerifyKeyEvent(&up_event, KeyboardCode::VKEY_CONTROL, dom_code, false, false);
  ASSERT_FALSE(up_event.IsControlDown());
}

TEST_F(ModifierKeyboardHookWinTest, SimpleRightControlKeypressTest) {
  const KeyboardCode key_code = KeyboardCode::VKEY_RCONTROL;
  const DomCode dom_code = DomCode::CONTROL_RIGHT;
  SendModifierKeyDownEvent(key_code, dom_code);
  ASSERT_EQ(key_events()->size(), 1ULL);
  SendModifierKeyUpEvent(key_code, dom_code);
  ASSERT_EQ(key_events()->size(), 2ULL);

  KeyEvent down_event = key_events()->at(0);
  VerifyKeyEvent(&down_event, KeyboardCode::VKEY_CONTROL, dom_code, true,
                 false);
  ASSERT_TRUE(down_event.IsControlDown());
  ASSERT_FALSE(down_event.IsAltDown());
  ASSERT_FALSE(down_event.IsCommandDown());

  KeyEvent up_event = key_events()->at(1);
  VerifyKeyEvent(&up_event, KeyboardCode::VKEY_CONTROL, dom_code, false, false);
  ASSERT_FALSE(up_event.IsControlDown());
}

TEST_F(ModifierKeyboardHookWinTest, RepeatingRightControlKeypressTest) {
  const int repeat_count = 10;
  const KeyboardCode key_code = KeyboardCode::VKEY_RCONTROL;
  const DomCode dom_code = DomCode::CONTROL_RIGHT;
  SendModifierKeyDownEvent(key_code, dom_code, repeat_count);
  ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count);
  SendModifierKeyUpEvent(key_code, dom_code);
  ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count + 1);

  bool should_repeat = false;
  for (int i = 0; i < repeat_count; i++) {
    KeyEvent event = key_events()->at(i);
    VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, dom_code, true,
                   should_repeat);
    ASSERT_TRUE(event.IsControlDown());
    ASSERT_FALSE(event.IsAltDown());
    ASSERT_FALSE(event.IsCommandDown());
    should_repeat = true;
  }

  KeyEvent up_event = key_events()->at(repeat_count);
  VerifyKeyEvent(&up_event, KeyboardCode::VKEY_CONTROL, dom_code, false, false);
  ASSERT_FALSE(up_event.IsControlDown());
}

TEST_F(ModifierKeyboardHookWinTest, SimpleLifoControlSequenceTest) {
  const KeyboardCode left_key_code = KeyboardCode::VKEY_LCONTROL;
  const DomCode left_dom_code = DomCode::CONTROL_LEFT;
  const KeyboardCode right_key_code = KeyboardCode::VKEY_RCONTROL;
  const DomCode right_dom_code = DomCode::CONTROL_RIGHT;
  SendModifierKeyDownEvent(left_key_code, left_dom_code, 2);
  SendModifierKeyDownEvent(right_key_code, right_dom_code, 2);
  SendModifierKeyUpEvent(right_key_code, right_dom_code);
  SendModifierKeyUpEvent(left_key_code, left_dom_code);
  ASSERT_EQ(key_events()->size(), 6ULL);

  // First key down, no repeat.
  KeyEvent event = key_events()->at(0);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, left_dom_code, true,
                 false);
  ASSERT_TRUE(event.IsControlDown());

  // First key still down, repeat.
  event = key_events()->at(1);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, left_dom_code, true, true);
  ASSERT_TRUE(event.IsControlDown());

  // Second key down, repeat.
  event = key_events()->at(2);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, right_dom_code, true,
                 true);
  ASSERT_TRUE(event.IsControlDown());

  // Second key still down, repeat.
  event = key_events()->at(3);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, right_dom_code, true,
                 true);
  ASSERT_TRUE(event.IsControlDown());

  // Second key up.
  event = key_events()->at(4);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, right_dom_code, false,
                 false);
  ASSERT_TRUE(event.IsControlDown());

  // First key up.
  event = key_events()->at(5);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, left_dom_code, false,
                 false);
  ASSERT_FALSE(event.IsControlDown());
}

TEST_F(ModifierKeyboardHookWinTest, SimpleFifoControlSequenceTest) {
  const KeyboardCode left_key_code = KeyboardCode::VKEY_LCONTROL;
  const DomCode left_dom_code = DomCode::CONTROL_LEFT;
  const KeyboardCode right_key_code = KeyboardCode::VKEY_RCONTROL;
  const DomCode right_dom_code = DomCode::CONTROL_RIGHT;
  SendModifierKeyDownEvent(right_key_code, right_dom_code, 2);
  SendModifierKeyDownEvent(left_key_code, left_dom_code, 2);
  SendModifierKeyUpEvent(right_key_code, right_dom_code);
  SendModifierKeyUpEvent(left_key_code, left_dom_code);
  ASSERT_EQ(key_events()->size(), 6ULL);

  // First key down, no repeat.
  KeyEvent event = key_events()->at(0);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, right_dom_code, true,
                 false);
  ASSERT_TRUE(event.IsControlDown());

  // First key still down, repeat.
  event = key_events()->at(1);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, right_dom_code, true,
                 true);
  ASSERT_TRUE(event.IsControlDown());

  // Second key down, repeat.
  event = key_events()->at(2);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, left_dom_code, true, true);
  ASSERT_TRUE(event.IsControlDown());

  // Second key still down, repeat.
  event = key_events()->at(3);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, left_dom_code, true, true);
  ASSERT_TRUE(event.IsControlDown());

  // First key up.
  event = key_events()->at(4);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, right_dom_code, false,
                 false);
  ASSERT_TRUE(event.IsControlDown());

  // Second key up.
  event = key_events()->at(5);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, left_dom_code, false,
                 false);
  ASSERT_FALSE(event.IsControlDown());
}

TEST_F(ModifierKeyboardHookWinTest, SimpleLeftAltKeypressTest) {
  const KeyboardCode key_code = KeyboardCode::VKEY_LMENU;
  const DomCode dom_code = DomCode::ALT_LEFT;
  SendModifierKeyDownEvent(key_code, dom_code);
  ASSERT_EQ(key_events()->size(), 1ULL);
  SendModifierKeyUpEvent(key_code, dom_code);
  ASSERT_EQ(key_events()->size(), 2ULL);

  KeyEvent down_event = key_events()->at(0);
  VerifyKeyEvent(&down_event, KeyboardCode::VKEY_MENU, dom_code, true, false);
  ASSERT_FALSE(down_event.IsControlDown());
  ASSERT_TRUE(down_event.IsAltDown());
  ASSERT_FALSE(down_event.IsCommandDown());

  KeyEvent up_event = key_events()->at(1);
  VerifyKeyEvent(&up_event, KeyboardCode::VKEY_MENU, dom_code, false, false);
  ASSERT_FALSE(up_event.IsAltDown());
}

TEST_F(ModifierKeyboardHookWinTest, RepeatingLeftAltKeypressTest) {
  const int repeat_count = 10;
  const KeyboardCode key_code = KeyboardCode::VKEY_LMENU;
  const DomCode dom_code = DomCode::ALT_LEFT;
  SendModifierKeyDownEvent(key_code, dom_code, repeat_count);
  ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count);
  SendModifierKeyUpEvent(key_code, dom_code);
  ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count + 1);

  bool should_repeat = false;
  for (int i = 0; i < repeat_count; i++) {
    KeyEvent event = key_events()->at(i);
    VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, dom_code, true,
                   should_repeat);
    ASSERT_FALSE(event.IsControlDown());
    ASSERT_TRUE(event.IsAltDown());
    ASSERT_FALSE(event.IsCommandDown());
    should_repeat = true;
  }

  KeyEvent up_event = key_events()->at(repeat_count);
  VerifyKeyEvent(&up_event, KeyboardCode::VKEY_MENU, dom_code, false, false);
  ASSERT_FALSE(up_event.IsAltDown());
}

TEST_F(ModifierKeyboardHookWinTest, SimpleRightAltKeypressTest) {
  const KeyboardCode key_code = KeyboardCode::VKEY_RMENU;
  const DomCode dom_code = DomCode::ALT_LEFT;
  SendModifierKeyDownEvent(key_code, dom_code);
  ASSERT_EQ(key_events()->size(), 1ULL);
  SendModifierKeyUpEvent(key_code, dom_code);
  ASSERT_EQ(key_events()->size(), 2ULL);

  KeyEvent down_event = key_events()->at(0);
  VerifyKeyEvent(&down_event, KeyboardCode::VKEY_MENU, dom_code, true, false);
  ASSERT_FALSE(down_event.IsControlDown());
  ASSERT_TRUE(down_event.IsAltDown());
  ASSERT_FALSE(down_event.IsCommandDown());

  KeyEvent up_event = key_events()->at(1);
  VerifyKeyEvent(&up_event, KeyboardCode::VKEY_MENU, dom_code, false, false);
  ASSERT_FALSE(up_event.IsAltDown());
}

TEST_F(ModifierKeyboardHookWinTest, RepeatingRightAltKeypressTest) {
  const int repeat_count = 10;
  const KeyboardCode key_code = KeyboardCode::VKEY_RMENU;
  const DomCode dom_code = DomCode::ALT_RIGHT;
  SendModifierKeyDownEvent(key_code, dom_code, repeat_count);
  ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count);
  SendModifierKeyUpEvent(key_code, dom_code);
  ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count + 1);

  bool should_repeat = false;
  for (int i = 0; i < repeat_count; i++) {
    KeyEvent event = key_events()->at(i);
    VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, dom_code, true,
                   should_repeat);
    ASSERT_FALSE(event.IsControlDown());
    ASSERT_TRUE(event.IsAltDown());
    ASSERT_FALSE(event.IsCommandDown());
    should_repeat = true;
  }

  KeyEvent up_event = key_events()->at(repeat_count);
  VerifyKeyEvent(&up_event, KeyboardCode::VKEY_MENU, dom_code, false, false);
  ASSERT_FALSE(up_event.IsAltDown());
}

TEST_F(ModifierKeyboardHookWinTest, SimpleLifoAltSequenceTest) {
  const KeyboardCode left_key_code = KeyboardCode::VKEY_LMENU;
  const DomCode left_dom_code = DomCode::ALT_LEFT;
  const KeyboardCode right_key_code = KeyboardCode::VKEY_RMENU;
  const DomCode right_dom_code = DomCode::ALT_RIGHT;
  SendModifierKeyDownEvent(left_key_code, left_dom_code, 2);
  SendModifierKeyDownEvent(right_key_code, right_dom_code, 2);
  SendModifierKeyUpEvent(right_key_code, right_dom_code);
  SendModifierKeyUpEvent(left_key_code, left_dom_code);
  ASSERT_EQ(key_events()->size(), 6ULL);

  // First key down, no repeat.
  KeyEvent event = key_events()->at(0);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, left_dom_code, true, false);
  ASSERT_TRUE(event.IsAltDown());

  // First key still down, repeat.
  event = key_events()->at(1);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, left_dom_code, true, true);
  ASSERT_TRUE(event.IsAltDown());

  // Second key down, repeat.
  event = key_events()->at(2);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, right_dom_code, true, true);
  ASSERT_TRUE(event.IsAltDown());

  // Second key still down, repeat.
  event = key_events()->at(3);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, right_dom_code, true, true);
  ASSERT_TRUE(event.IsAltDown());

  // Second key up.
  event = key_events()->at(4);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, right_dom_code, false, false);
  ASSERT_TRUE(event.IsAltDown());

  // First key up.
  event = key_events()->at(5);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, left_dom_code, false, false);
  ASSERT_FALSE(event.IsAltDown());
}

TEST_F(ModifierKeyboardHookWinTest, SimpleFifoAltSequenceTest) {
  const KeyboardCode left_key_code = KeyboardCode::VKEY_LMENU;
  const DomCode left_dom_code = DomCode::ALT_LEFT;
  const KeyboardCode right_key_code = KeyboardCode::VKEY_RMENU;
  const DomCode right_dom_code = DomCode::ALT_RIGHT;
  SendModifierKeyDownEvent(right_key_code, right_dom_code, 2);
  SendModifierKeyDownEvent(left_key_code, left_dom_code, 2);
  SendModifierKeyUpEvent(right_key_code, right_dom_code);
  SendModifierKeyUpEvent(left_key_code, left_dom_code);
  ASSERT_EQ(key_events()->size(), 6ULL);

  // First key down, no repeat.
  KeyEvent event = key_events()->at(0);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, right_dom_code, true, false);
  ASSERT_TRUE(event.IsAltDown());

  // First key still down, repeat.
  event = key_events()->at(1);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, right_dom_code, true, true);
  ASSERT_TRUE(event.IsAltDown());

  // Second key down, repeat.
  event = key_events()->at(2);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, left_dom_code, true, true);
  ASSERT_TRUE(event.IsAltDown());

  // Second key still down, repeat.
  event = key_events()->at(3);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, left_dom_code, true, true);
  ASSERT_TRUE(event.IsAltDown());

  // First key up.
  event = key_events()->at(4);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, right_dom_code, false, false);
  ASSERT_TRUE(event.IsAltDown());

  // Second key up.
  event = key_events()->at(5);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, left_dom_code, false, false);
  ASSERT_FALSE(event.IsAltDown());
}

TEST_F(ModifierKeyboardHookWinTest, SimpleLeftWinKeypressTest) {
  const KeyboardCode key_code = KeyboardCode::VKEY_LWIN;
  const DomCode dom_code = DomCode::META_LEFT;
  SendModifierKeyDownEvent(key_code, dom_code);
  ASSERT_EQ(key_events()->size(), 1ULL);
  SendModifierKeyUpEvent(key_code, dom_code);
  ASSERT_EQ(key_events()->size(), 2ULL);

  KeyEvent down_event = key_events()->at(0);
  // VKEY_LWIN is the 'non-located' version of the Windows key.
  VerifyKeyEvent(&down_event, KeyboardCode::VKEY_LWIN, dom_code, true, false);
  ASSERT_FALSE(down_event.IsControlDown());
  ASSERT_FALSE(down_event.IsAltDown());
  ASSERT_TRUE(down_event.IsCommandDown());

  KeyEvent up_event = key_events()->at(1);
  VerifyKeyEvent(&up_event, KeyboardCode::VKEY_LWIN, dom_code, false, false);
  ASSERT_FALSE(up_event.IsCommandDown());
}

TEST_F(ModifierKeyboardHookWinTest, RepeatingLeftWinKeypressTest) {
  const int repeat_count = 10;
  const KeyboardCode key_code = KeyboardCode::VKEY_LWIN;
  const DomCode dom_code = DomCode::META_LEFT;
  SendModifierKeyDownEvent(key_code, dom_code, repeat_count);
  ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count);
  SendModifierKeyUpEvent(key_code, dom_code);
  ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count + 1);

  bool should_repeat = false;
  for (int i = 0; i < repeat_count; i++) {
    KeyEvent event = key_events()->at(i);
    // VKEY_LWIN is the 'non-located' version of the Windows key.
    VerifyKeyEvent(&event, KeyboardCode::VKEY_LWIN, dom_code, true,
                   should_repeat);
    ASSERT_FALSE(event.IsControlDown());
    ASSERT_FALSE(event.IsAltDown());
    ASSERT_TRUE(event.IsCommandDown());
    should_repeat = true;
  }

  KeyEvent up_event = key_events()->at(repeat_count);
  VerifyKeyEvent(&up_event, KeyboardCode::VKEY_LWIN, dom_code, false, false);
  ASSERT_FALSE(up_event.IsCommandDown());
}

TEST_F(ModifierKeyboardHookWinTest, SimpleRightWinKeypressTest) {
  const KeyboardCode key_code = KeyboardCode::VKEY_RWIN;
  const DomCode dom_code = DomCode::META_RIGHT;
  SendModifierKeyDownEvent(key_code, dom_code);
  ASSERT_EQ(key_events()->size(), 1ULL);
  SendModifierKeyUpEvent(key_code, dom_code);
  ASSERT_EQ(key_events()->size(), 2ULL);

  KeyEvent down_event = key_events()->at(0);
  // VKEY_LWIN is the 'non-located' version of the Windows key.
  VerifyKeyEvent(&down_event, KeyboardCode::VKEY_RWIN, dom_code, true, false);
  ASSERT_FALSE(down_event.IsControlDown());
  ASSERT_FALSE(down_event.IsAltDown());
  ASSERT_TRUE(down_event.IsCommandDown());

  KeyEvent up_event = key_events()->at(1);
  VerifyKeyEvent(&up_event, KeyboardCode::VKEY_RWIN, dom_code, false, false);
  ASSERT_FALSE(up_event.IsCommandDown());
}

TEST_F(ModifierKeyboardHookWinTest, RepeatingRightWinKeypressTest) {
  const int repeat_count = 10;
  const KeyboardCode key_code = KeyboardCode::VKEY_RWIN;
  const DomCode dom_code = DomCode::META_RIGHT;
  SendModifierKeyDownEvent(key_code, dom_code, repeat_count);
  ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count);
  SendModifierKeyUpEvent(key_code, dom_code);
  ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count + 1);

  bool should_repeat = false;
  for (int i = 0; i < repeat_count; i++) {
    KeyEvent event = key_events()->at(i);
    // VKEY_LWIN is the 'non-located' version of the Windows key.
    VerifyKeyEvent(&event, KeyboardCode::VKEY_RWIN, dom_code, true,
                   should_repeat);
    ASSERT_FALSE(event.IsControlDown());
    ASSERT_FALSE(event.IsAltDown());
    ASSERT_TRUE(event.IsCommandDown());
    should_repeat = true;
  }

  KeyEvent up_event = key_events()->at(repeat_count);
  VerifyKeyEvent(&up_event, KeyboardCode::VKEY_RWIN, dom_code, false, false);
  ASSERT_FALSE(up_event.IsCommandDown());
}

TEST_F(ModifierKeyboardHookWinTest, SimpleLifoWinSequenceTest) {
  const KeyboardCode left_key_code = KeyboardCode::VKEY_LWIN;
  const DomCode left_dom_code = DomCode::META_LEFT;
  const KeyboardCode right_key_code = KeyboardCode::VKEY_RWIN;
  const DomCode right_dom_code = DomCode::META_RIGHT;
  SendModifierKeyDownEvent(left_key_code, left_dom_code, 2);
  SendModifierKeyDownEvent(right_key_code, right_dom_code, 2);
  SendModifierKeyUpEvent(right_key_code, right_dom_code);
  SendModifierKeyUpEvent(left_key_code, left_dom_code);
  ASSERT_EQ(key_events()->size(), 6ULL);

  // First key down, no repeat.
  KeyEvent event = key_events()->at(0);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_LWIN, left_dom_code, true, false);
  ASSERT_TRUE(event.IsCommandDown());

  // First key still down, repeat.
  event = key_events()->at(1);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_LWIN, left_dom_code, true, true);
  ASSERT_TRUE(event.IsCommandDown());

  // Second key down, no repeat.
  event = key_events()->at(2);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_RWIN, right_dom_code, true, false);
  ASSERT_TRUE(event.IsCommandDown());

  // Second key still down, repeat.
  event = key_events()->at(3);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_RWIN, right_dom_code, true, true);
  ASSERT_TRUE(event.IsCommandDown());

  // Second key up.
  event = key_events()->at(4);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_RWIN, right_dom_code, false, false);
  ASSERT_TRUE(event.IsCommandDown());

  // First key up.
  event = key_events()->at(5);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_LWIN, left_dom_code, false, false);
  ASSERT_FALSE(event.IsCommandDown());
}

TEST_F(ModifierKeyboardHookWinTest, SimpleFifoWinSequenceTest) {
  const KeyboardCode left_key_code = KeyboardCode::VKEY_LWIN;
  const DomCode left_dom_code = DomCode::META_LEFT;
  const KeyboardCode right_key_code = KeyboardCode::VKEY_RWIN;
  const DomCode right_dom_code = DomCode::META_RIGHT;
  SendModifierKeyDownEvent(right_key_code, right_dom_code, 2);
  SendModifierKeyDownEvent(left_key_code, left_dom_code, 2);
  SendModifierKeyUpEvent(right_key_code, right_dom_code);
  SendModifierKeyUpEvent(left_key_code, left_dom_code);
  ASSERT_EQ(key_events()->size(), 6ULL);

  // First key down, no repeat.
  KeyEvent event = key_events()->at(0);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_RWIN, right_dom_code, true, false);
  ASSERT_TRUE(event.IsCommandDown());

  // First key still down, repeat.
  event = key_events()->at(1);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_RWIN, right_dom_code, true, true);
  ASSERT_TRUE(event.IsCommandDown());

  // Second key down, no repeat.
  event = key_events()->at(2);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_LWIN, left_dom_code, true, false);
  ASSERT_TRUE(event.IsCommandDown());

  // Second key still down, repeat.
  event = key_events()->at(3);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_LWIN, left_dom_code, true, true);
  ASSERT_TRUE(event.IsCommandDown());

  // First key up.
  event = key_events()->at(4);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_RWIN, right_dom_code, false, false);
  ASSERT_TRUE(event.IsCommandDown());

  // Second key up.
  event = key_events()->at(5);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_LWIN, left_dom_code, false, false);
  ASSERT_FALSE(event.IsCommandDown());
}

TEST_F(ModifierKeyboardHookWinTest, CombinedModifierLifoSequenceKeypressTest) {
  const KeyboardCode first_key_code = KeyboardCode::VKEY_LCONTROL;
  const DomCode first_dom_code = DomCode::CONTROL_LEFT;
  const KeyboardCode second_key_code = KeyboardCode::VKEY_RWIN;
  const DomCode second_dom_code = DomCode::META_RIGHT;
  const KeyboardCode third_key_code = KeyboardCode::VKEY_LMENU;
  const DomCode third_dom_code = DomCode::ALT_LEFT;
  SendModifierKeyDownEvent(first_key_code, first_dom_code, 2);
  SendModifierKeyDownEvent(second_key_code, second_dom_code, 2);
  SendModifierKeyDownEvent(third_key_code, third_dom_code, 2);
  SendModifierKeyUpEvent(third_key_code, third_dom_code);
  SendModifierKeyUpEvent(second_key_code, second_dom_code);
  SendModifierKeyUpEvent(first_key_code, first_dom_code);
  ASSERT_EQ(key_events()->size(), 9ULL);

  // First key down, no repeat.
  KeyEvent event = key_events()->at(0);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, first_dom_code, true,
                 false);
  ASSERT_TRUE(event.IsControlDown());
  ASSERT_FALSE(event.IsAltDown());
  ASSERT_FALSE(event.IsCommandDown());

  // First key still down, repeat.
  event = key_events()->at(1);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, first_dom_code, true,
                 true);
  ASSERT_TRUE(event.IsControlDown());
  ASSERT_FALSE(event.IsAltDown());
  ASSERT_FALSE(event.IsCommandDown());

  // Second key down, no repeat.
  event = key_events()->at(2);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_RWIN, second_dom_code, true, false);
  ASSERT_TRUE(event.IsControlDown());
  ASSERT_FALSE(event.IsAltDown());
  ASSERT_TRUE(event.IsCommandDown());

  // Second key still down, repeat.
  event = key_events()->at(3);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_RWIN, second_dom_code, true, true);
  ASSERT_TRUE(event.IsControlDown());
  ASSERT_FALSE(event.IsAltDown());
  ASSERT_TRUE(event.IsCommandDown());

  // Third key down, no repeat.
  event = key_events()->at(4);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, third_dom_code, true, false);
  ASSERT_TRUE(event.IsControlDown());
  ASSERT_TRUE(event.IsAltDown());
  ASSERT_TRUE(event.IsCommandDown());

  // Third key still down, repeat.
  event = key_events()->at(5);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, third_dom_code, true, true);
  ASSERT_TRUE(event.IsControlDown());
  ASSERT_TRUE(event.IsAltDown());
  ASSERT_TRUE(event.IsCommandDown());

  // Third key up.
  event = key_events()->at(6);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, third_dom_code, false, false);
  ASSERT_TRUE(event.IsControlDown());
  ASSERT_FALSE(event.IsAltDown());
  ASSERT_TRUE(event.IsCommandDown());

  // Second key up.
  event = key_events()->at(7);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_RWIN, second_dom_code, false,
                 false);
  ASSERT_TRUE(event.IsControlDown());
  ASSERT_FALSE(event.IsAltDown());
  ASSERT_FALSE(event.IsCommandDown());

  // First key up.
  event = key_events()->at(8);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, first_dom_code, false,
                 false);
  ASSERT_FALSE(event.IsControlDown());
  ASSERT_FALSE(event.IsAltDown());
  ASSERT_FALSE(event.IsCommandDown());
}

TEST_F(ModifierKeyboardHookWinTest, CombinedModifierFifoSequenceKeypressTest) {
  const KeyboardCode first_key_code = KeyboardCode::VKEY_RCONTROL;
  const DomCode first_dom_code = DomCode::CONTROL_RIGHT;
  const KeyboardCode second_key_code = KeyboardCode::VKEY_LWIN;
  const DomCode second_dom_code = DomCode::META_LEFT;
  const KeyboardCode third_key_code = KeyboardCode::VKEY_RMENU;
  const DomCode third_dom_code = DomCode::ALT_RIGHT;
  SendModifierKeyDownEvent(first_key_code, first_dom_code, 2);
  SendModifierKeyDownEvent(second_key_code, second_dom_code, 2);
  SendModifierKeyDownEvent(third_key_code, third_dom_code, 2);
  SendModifierKeyUpEvent(first_key_code, first_dom_code);
  SendModifierKeyUpEvent(second_key_code, second_dom_code);
  SendModifierKeyUpEvent(third_key_code, third_dom_code);
  ASSERT_EQ(key_events()->size(), 9ULL);

  // First key down, no repeat.
  KeyEvent event = key_events()->at(0);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, first_dom_code, true,
                 false);
  ASSERT_TRUE(event.IsControlDown());
  ASSERT_FALSE(event.IsAltDown());
  ASSERT_FALSE(event.IsCommandDown());

  // First key still down, repeat.
  event = key_events()->at(1);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, first_dom_code, true,
                 true);
  ASSERT_TRUE(event.IsControlDown());
  ASSERT_FALSE(event.IsAltDown());
  ASSERT_FALSE(event.IsCommandDown());

  // Second key down, no repeat.
  event = key_events()->at(2);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_LWIN, second_dom_code, true, false);
  ASSERT_TRUE(event.IsControlDown());
  ASSERT_FALSE(event.IsAltDown());
  ASSERT_TRUE(event.IsCommandDown());

  // Second key still down, repeat.
  event = key_events()->at(3);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_LWIN, second_dom_code, true, true);
  ASSERT_TRUE(event.IsControlDown());
  ASSERT_FALSE(event.IsAltDown());
  ASSERT_TRUE(event.IsCommandDown());

  // Third key down, no repeat.
  event = key_events()->at(4);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, third_dom_code, true, false);
  ASSERT_TRUE(event.IsControlDown());
  ASSERT_TRUE(event.IsAltDown());
  ASSERT_TRUE(event.IsCommandDown());

  // Third key still down, repeat.
  event = key_events()->at(5);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, third_dom_code, true, true);
  ASSERT_TRUE(event.IsControlDown());
  ASSERT_TRUE(event.IsAltDown());
  ASSERT_TRUE(event.IsCommandDown());

  // First key up.
  event = key_events()->at(6);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, first_dom_code, false,
                 false);
  ASSERT_FALSE(event.IsControlDown());
  ASSERT_TRUE(event.IsAltDown());
  ASSERT_TRUE(event.IsCommandDown());

  // Second key up.
  event = key_events()->at(7);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_LWIN, second_dom_code, false,
                 false);
  ASSERT_FALSE(event.IsControlDown());
  ASSERT_TRUE(event.IsAltDown());
  ASSERT_FALSE(event.IsCommandDown());

  // Third key up.
  event = key_events()->at(8);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, third_dom_code, false, false);
  ASSERT_FALSE(event.IsControlDown());
  ASSERT_FALSE(event.IsAltDown());
  ASSERT_FALSE(event.IsCommandDown());
}

TEST_F(ModifierKeyboardHookWinTest, VerifyPlatformModifierStateTest) {
  SendModifierKeyDownEvent(KeyboardCode::VKEY_LCONTROL, DomCode::CONTROL_LEFT);
  ASSERT_TRUE(win::IsCtrlPressed());
  ASSERT_FALSE(win::IsAltPressed());
  ASSERT_FALSE(win::IsWindowsKeyPressed());

  SendModifierKeyDownEvent(KeyboardCode::VKEY_RWIN, DomCode::META_RIGHT);
  ASSERT_TRUE(win::IsCtrlPressed());
  ASSERT_FALSE(win::IsAltPressed());
  ASSERT_TRUE(win::IsWindowsKeyPressed());

  SendModifierKeyDownEvent(KeyboardCode::VKEY_LMENU, DomCode::ALT_LEFT);
  ASSERT_TRUE(win::IsCtrlPressed());
  ASSERT_TRUE(win::IsAltPressed());
  ASSERT_FALSE(win::IsAltRightPressed());
  ASSERT_TRUE(win::IsWindowsKeyPressed());

  SendModifierKeyUpEvent(KeyboardCode::VKEY_RWIN, DomCode::META_RIGHT);
  ASSERT_TRUE(win::IsCtrlPressed());
  ASSERT_TRUE(win::IsAltPressed());
  ASSERT_FALSE(win::IsWindowsKeyPressed());

  SendModifierKeyUpEvent(KeyboardCode::VKEY_LCONTROL, DomCode::CONTROL_LEFT);
  ASSERT_FALSE(win::IsCtrlPressed());
  ASSERT_TRUE(win::IsAltPressed());
  ASSERT_FALSE(win::IsWindowsKeyPressed());

  SendModifierKeyUpEvent(KeyboardCode::VKEY_LMENU, DomCode::ALT_LEFT);
  ASSERT_FALSE(win::IsCtrlPressed());
  ASSERT_FALSE(win::IsAltPressed());
  ASSERT_FALSE(win::IsWindowsKeyPressed());

  SendModifierKeyDownEvent(KeyboardCode::VKEY_RMENU, DomCode::ALT_RIGHT);
  ASSERT_TRUE(win::IsAltPressed());
  ASSERT_TRUE(win::IsAltRightPressed());
}

TEST_F(ModifierKeyboardHookWinTest, SimpleAltGrKeyPressTest) {
  ScopedKeyboardLayout keyboard_layout(KeyboardLayout::KEYBOARD_LAYOUT_GERMAN);

  // AltGr produces two events, an injected, modified scan code for VK_LCONTROL,
  // and an event for VK_RMENU.  We simulate that sequence here.
  const KeyboardCode altgr_key_code = KeyboardCode::VKEY_RMENU;
  const DomCode altgr_dom_code = DomCode::ALT_RIGHT;
  const DomCode control_dom_code = DomCode::CONTROL_LEFT;
  const DWORD control_scan_code =
      KeycodeConverter::DomCodeToNativeKeycode(control_dom_code);
  const DWORD injected_control_scan_code = control_scan_code | 0x0200;

  ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage(
      WM_KEYDOWN, KeyboardCode::VKEY_LCONTROL, injected_control_scan_code,
      next_time_stamp()));
  SendModifierKeyDownEvent(altgr_key_code, altgr_dom_code);

  ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage(
      WM_KEYUP, KeyboardCode::VKEY_LCONTROL,
      KeycodeConverter::DomCodeToNativeKeycode(control_dom_code),
      next_time_stamp()));
  SendModifierKeyUpEvent(altgr_key_code, altgr_dom_code);
  ASSERT_EQ(key_events()->size(), 4ULL);

  // Injected control key down, no repeat.
  KeyEvent event = key_events()->at(0);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, control_dom_code, true,
                 false);
  ASSERT_TRUE(event.IsControlDown());
  ASSERT_FALSE(event.IsAltDown());
  ASSERT_FALSE(event.IsAltGrDown());
  ASSERT_FALSE(event.IsCommandDown());

  // Altgr key down.
  event = key_events()->at(1);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, altgr_dom_code, true, false);
  ASSERT_FALSE(event.IsControlDown());
  ASSERT_FALSE(event.IsAltDown());
  ASSERT_TRUE(event.IsAltGrDown());
  ASSERT_FALSE(event.IsCommandDown());

  // Injected control key up.
  event = key_events()->at(2);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, control_dom_code, false,
                 false);
  ASSERT_FALSE(event.IsControlDown());
  ASSERT_FALSE(event.IsAltDown());
  ASSERT_TRUE(event.IsAltGrDown());
  ASSERT_FALSE(event.IsCommandDown());

  // AltGr key up.
  event = key_events()->at(3);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, altgr_dom_code, false, false);
  ASSERT_FALSE(event.IsControlDown());
  ASSERT_FALSE(event.IsAltDown());
  ASSERT_FALSE(event.IsAltGrDown());
  ASSERT_FALSE(event.IsCommandDown());
}

TEST_F(ModifierKeyboardHookWinTest, RepeatingAltGrKeyPressTest) {
  ScopedKeyboardLayout keyboard_layout(KeyboardLayout::KEYBOARD_LAYOUT_GERMAN);

  // AltGr produces two events, an injected, modified scan code for VK_LCONTROL,
  // and an event for VK_RMENU.  This sequence repeats for each repeated key
  // press.  We simulate that sequence here.
  const KeyboardCode altgr_key_code = KeyboardCode::VKEY_RMENU;
  const DomCode altgr_dom_code = DomCode::ALT_RIGHT;
  const DomCode control_dom_code = DomCode::CONTROL_LEFT;
  const DWORD control_scan_code =
      KeycodeConverter::DomCodeToNativeKeycode(control_dom_code);
  const DWORD injected_control_scan_code = control_scan_code | 0x0200;

  ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage(
      WM_KEYDOWN, KeyboardCode::VKEY_LCONTROL, injected_control_scan_code,
      next_time_stamp()));
  SendModifierKeyDownEvent(altgr_key_code, altgr_dom_code);
  ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage(
      WM_KEYDOWN, KeyboardCode::VKEY_LCONTROL, injected_control_scan_code,
      next_time_stamp()));
  SendModifierKeyDownEvent(altgr_key_code, altgr_dom_code);
  ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage(
      WM_KEYDOWN, KeyboardCode::VKEY_LCONTROL, injected_control_scan_code,
      next_time_stamp()));
  SendModifierKeyDownEvent(altgr_key_code, altgr_dom_code);
  ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage(
      WM_KEYUP, KeyboardCode::VKEY_LCONTROL,
      KeycodeConverter::DomCodeToNativeKeycode(control_dom_code),
      next_time_stamp()));
  SendModifierKeyUpEvent(altgr_key_code, altgr_dom_code);
  ASSERT_EQ(key_events()->size(), 8ULL);

  // Injected control key down, no repeat.
  KeyEvent event = key_events()->at(0);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, control_dom_code, true,
                 false);
  ASSERT_TRUE(event.IsControlDown());
  ASSERT_FALSE(event.IsAltDown());
  ASSERT_FALSE(event.IsAltGrDown());
  ASSERT_FALSE(event.IsCommandDown());

  // Altgr key down.
  event = key_events()->at(1);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, altgr_dom_code, true, false);
  ASSERT_FALSE(event.IsControlDown());
  ASSERT_FALSE(event.IsAltDown());
  ASSERT_TRUE(event.IsAltGrDown());
  ASSERT_FALSE(event.IsCommandDown());

  // Injected control key down, repeat.
  event = key_events()->at(2);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, control_dom_code, true,
                 true);
  ASSERT_FALSE(event.IsControlDown());
  ASSERT_FALSE(event.IsAltDown());
  ASSERT_TRUE(event.IsAltGrDown());
  ASSERT_FALSE(event.IsCommandDown());

  // Altgr key down, repeat.
  event = key_events()->at(3);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, altgr_dom_code, true, true);
  ASSERT_FALSE(event.IsControlDown());
  ASSERT_FALSE(event.IsAltDown());
  ASSERT_TRUE(event.IsAltGrDown());
  ASSERT_FALSE(event.IsCommandDown());

  // Injected control key down, repeat.
  event = key_events()->at(4);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, control_dom_code, true,
                 true);
  ASSERT_FALSE(event.IsControlDown());
  ASSERT_FALSE(event.IsAltDown());
  ASSERT_TRUE(event.IsAltGrDown());
  ASSERT_FALSE(event.IsCommandDown());

  // Altgr key down, repeat.
  event = key_events()->at(5);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, altgr_dom_code, true, true);
  ASSERT_FALSE(event.IsControlDown());
  ASSERT_FALSE(event.IsAltDown());
  ASSERT_TRUE(event.IsAltGrDown());
  ASSERT_FALSE(event.IsCommandDown());

  // Injected control key up.
  event = key_events()->at(6);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, control_dom_code, false,
                 false);
  ASSERT_FALSE(event.IsControlDown());
  ASSERT_FALSE(event.IsAltDown());
  ASSERT_TRUE(event.IsAltGrDown());
  ASSERT_FALSE(event.IsCommandDown());

  // AltGr key up.
  event = key_events()->at(7);
  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, altgr_dom_code, false, false);
  ASSERT_FALSE(event.IsControlDown());
  ASSERT_FALSE(event.IsAltDown());
  ASSERT_FALSE(event.IsAltGrDown());
  ASSERT_FALSE(event.IsCommandDown());
}

TEST_F(ModifierKeyboardHookWinTest, VerifyAltGrPlatformModifierStateTest) {
  ScopedKeyboardLayout keyboard_layout(KeyboardLayout::KEYBOARD_LAYOUT_GERMAN);

  // AltGr produces two events, an injected, modified scan code for VK_LCONTROL,
  // and an event for VK_RMENU.  We simulate that sequence here.
  const KeyboardCode altgr_key_code = KeyboardCode::VKEY_RMENU;
  const DomCode altgr_dom_code = DomCode::ALT_RIGHT;
  const DomCode control_dom_code = DomCode::CONTROL_LEFT;
  const DWORD control_scan_code =
      KeycodeConverter::DomCodeToNativeKeycode(control_dom_code);
  const DWORD injected_control_scan_code = control_scan_code | 0x0200;

  ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage(
      WM_KEYDOWN, KeyboardCode::VKEY_LCONTROL, injected_control_scan_code,
      next_time_stamp()));
  ASSERT_TRUE(win::IsCtrlPressed());
  ASSERT_FALSE(win::IsAltPressed());
  ASSERT_FALSE(win::IsAltRightPressed());
  ASSERT_FALSE(win::IsWindowsKeyPressed());

  SendModifierKeyDownEvent(altgr_key_code, altgr_dom_code);
  ASSERT_TRUE(win::IsCtrlPressed());
  ASSERT_TRUE(win::IsAltPressed());
  ASSERT_TRUE(win::IsAltRightPressed());
  ASSERT_FALSE(win::IsWindowsKeyPressed());

  ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage(
      WM_KEYUP, KeyboardCode::VKEY_LCONTROL,
      KeycodeConverter::DomCodeToNativeKeycode(control_dom_code),
      next_time_stamp()));
  ASSERT_FALSE(win::IsCtrlPressed());
  ASSERT_TRUE(win::IsAltPressed());
  ASSERT_TRUE(win::IsAltRightPressed());
  ASSERT_FALSE(win::IsWindowsKeyPressed());

  SendModifierKeyUpEvent(altgr_key_code, altgr_dom_code);
  ASSERT_FALSE(win::IsCtrlPressed());
  ASSERT_FALSE(win::IsAltPressed());
  ASSERT_FALSE(win::IsAltRightPressed());
  ASSERT_FALSE(win::IsWindowsKeyPressed());
}

TEST_F(ModifierKeyboardHookWinTest, NonInterceptedKeysTest) {
  // Here we try a few keys we do not expect to be intercepted / handled.
  ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage(
      WM_KEYDOWN, KeyboardCode::VKEY_RSHIFT,
      KeycodeConverter::DomCodeToNativeKeycode(DomCode::SHIFT_RIGHT),
      next_time_stamp()));
  ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage(
      WM_KEYUP, KeyboardCode::VKEY_RSHIFT,
      KeycodeConverter::DomCodeToNativeKeycode(DomCode::SHIFT_RIGHT),
      next_time_stamp()));

  ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage(
      WM_KEYDOWN, KeyboardCode::VKEY_MEDIA_PLAY_PAUSE,
      KeycodeConverter::DomCodeToNativeKeycode(DomCode::MEDIA_PLAY_PAUSE),
      next_time_stamp()));
  ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage(
      WM_KEYUP, KeyboardCode::VKEY_MEDIA_PLAY_PAUSE,
      KeycodeConverter::DomCodeToNativeKeycode(DomCode::MEDIA_PLAY_PAUSE),
      next_time_stamp()));

  ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage(
      WM_KEYDOWN, KeyboardCode::VKEY_A,
      KeycodeConverter::DomCodeToNativeKeycode(DomCode::US_A),
      next_time_stamp()));
  ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage(
      WM_KEYUP, KeyboardCode::VKEY_A,
      KeycodeConverter::DomCodeToNativeKeycode(DomCode::US_A),
      next_time_stamp()));
}

}  // namespace ui