blob: bf82ee0a4e4c70dc3967856b91f384a8423d861d [file] [log] [blame]
// Copyright 2010-2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "unix/ibus/key_event_handler.h"
#include <map>
#include <memory>
#include "base/port.h"
#include "base/util.h"
#include "config/config.pb.h"
#include "session/commands.pb.h"
#include "session/key_event_util.h"
#include "testing/base/public/gunit.h"
using std::unique_ptr;
namespace mozc {
namespace ibus {
class KeyEventHandlerTest : public testing::Test {
protected:
virtual void SetUp() {
handler_.reset(new KeyEventHandler);
keyval_to_modifier_.clear();
keyval_to_modifier_[IBUS_Shift_L] = commands::KeyEvent::LEFT_SHIFT;
keyval_to_modifier_[IBUS_Shift_R] = commands::KeyEvent::RIGHT_SHIFT;
keyval_to_modifier_[IBUS_Control_L] = commands::KeyEvent::LEFT_CTRL;
keyval_to_modifier_[IBUS_Control_R] = commands::KeyEvent::RIGHT_CTRL;
keyval_to_modifier_[IBUS_Alt_L] = commands::KeyEvent::LEFT_ALT;
keyval_to_modifier_[IBUS_Alt_R] = commands::KeyEvent::RIGHT_ALT;
}
// Currently this function does not supports special keys.
void AppendToKeyEvent(guint keyval, commands::KeyEvent *key) const {
const map<guint, commands::KeyEvent::ModifierKey>::const_iterator it =
keyval_to_modifier_.find(keyval);
if (it != keyval_to_modifier_.end()) {
key->add_modifier_keys(it->second);
} else {
key->set_key_code(keyval);
}
}
bool ProcessKey(bool is_key_up, guint keyval, commands::KeyEvent *key) {
AppendToKeyEvent(keyval, key);
return handler_->ProcessModifiers(is_key_up, keyval, key);
}
bool ProcessKeyWithCapsLock(bool is_key_up, guint keyval,
commands::KeyEvent *key) {
key->add_modifier_keys(commands::KeyEvent::CAPS);
return ProcessKey(is_key_up, keyval, key);
}
bool IsPressed(guint keyval) const {
const set<guint> &pressed_set = handler_->currently_pressed_modifiers_;
return pressed_set.find(keyval) != pressed_set.end();
}
bool is_non_modifier_key_pressed() {
return handler_->is_non_modifier_key_pressed_;
}
const set<guint> &currently_pressed_modifiers() {
return handler_->currently_pressed_modifiers_;
}
const set<commands::KeyEvent::ModifierKey> &modifiers_to_be_sent() {
return handler_->modifiers_to_be_sent_;
}
testing::AssertionResult CheckModifiersToBeSent(uint32 modifiers) {
uint32 to_be_sent_mask = 0;
for (set<commands::KeyEvent::ModifierKey>::iterator it =
modifiers_to_be_sent().begin();
it != modifiers_to_be_sent().end(); ++it) {
to_be_sent_mask |= *it;
}
if (modifiers != to_be_sent_mask) {
return testing::AssertionFailure()
<< "\n"
<< "Expected: " << modifiers << "\n"
<< " Actual: " << to_be_sent_mask << "\n";
}
return testing::AssertionSuccess();
}
testing::AssertionResult CheckModifiersPressed(bool expect_pressed) {
bool pressed = !currently_pressed_modifiers().empty();
if (pressed == expect_pressed) {
return testing::AssertionSuccess();
} else {
return testing::AssertionFailure();
}
}
unique_ptr<KeyEventHandler> handler_;
map<guint, commands::KeyEvent::ModifierKey> keyval_to_modifier_;
};
#define EXPECT_MODIFIERS_TO_BE_SENT(modifiers) \
EXPECT_TRUE(CheckModifiersToBeSent(modifiers))
#define EXPECT_MODIFIERS_PRESSED() \
EXPECT_TRUE(CheckModifiersPressed(true))
#define EXPECT_NO_MODIFIERS_PRESSED() \
EXPECT_TRUE(CheckModifiersPressed(false))
namespace {
const uint32 kNoModifiers = 0;
const guint kDummyKeycode = 0;
} // namespace
TEST_F(KeyEventHandlerTest, GetKeyEvent) {
commands::KeyEvent key;
{ // Alt down => a down => a up => Alt up
key.Clear();
EXPECT_FALSE(handler_->GetKeyEvent(
IBUS_Alt_L, kDummyKeycode, IBUS_MOD1_MASK,
config::Config::ROMAN, true, &key));
EXPECT_MODIFIERS_TO_BE_SENT(
(commands::KeyEvent::LEFT_ALT | commands::KeyEvent::ALT));
EXPECT_MODIFIERS_PRESSED();
key.Clear();
EXPECT_TRUE(handler_->GetKeyEvent(
IBUS_a, 'a', IBUS_MOD1_MASK, config::Config::ROMAN, true, &key));
EXPECT_MODIFIERS_TO_BE_SENT(kNoModifiers);
EXPECT_MODIFIERS_PRESSED();
key.Clear();
EXPECT_FALSE(handler_->GetKeyEvent(
IBUS_a, 'a', (IBUS_MOD1_MASK | IBUS_RELEASE_MASK),
config::Config::ROMAN, true, &key));
EXPECT_MODIFIERS_TO_BE_SENT(kNoModifiers);
EXPECT_MODIFIERS_PRESSED();
key.Clear();
EXPECT_FALSE(handler_->GetKeyEvent(
IBUS_Alt_L, kDummyKeycode, (IBUS_MOD1_MASK | IBUS_RELEASE_MASK),
config::Config::ROMAN, true, &key));
EXPECT_MODIFIERS_TO_BE_SENT(kNoModifiers);
EXPECT_NO_MODIFIERS_PRESSED();
}
// This test fails in current implementation.
// TODO(hsumita): Enables it.
/*
{ // a down => Alt down => Alt up => a up
key.Clear();
EXPECT_TRUE(handler_->GetKeyEvent(
IBUS_a, 'a', kDummyKeycode, config::Config::ROMAN, true, &key));
EXPECT_MODIFIERS_TO_BE_SENT(kNoModifiers);
EXPECT_NO_MODIFIERS_PRESSED();
key.Clear();
EXPECT_FALSE(handler_->GetKeyEvent(
IBUS_Alt_L, kDummyKeycode, IBUS_MOD1_MASK,
config::Config::ROMAN, true, &key));
EXPECT_MODIFIERS_TO_BE_SENT(kNoModifiers);
EXPECT_MODIFIERS_PRESSED();
key.Clear();
EXPECT_TRUE(handler_->GetKeyEvent(
IBUS_Alt_L, kDummyKeycode, (IBUS_MOD1_MASK | IBUS_RELEASE_MASK),
config::Config::ROMAN, true, &key));
EXPECT_MODIFIERS_TO_BE_SENT(kNoModifiers);
EXPECT_NO_MODIFIERS_PRESSED();
key.Clear();
EXPECT_TRUE(handler_->GetKeyEvent(
IBUS_a, 'a', IBUS_RELEASE_MASK, config::Config::ROMAN, true, &key));
EXPECT_MODIFIERS_TO_BE_SENT(kNoModifiers);
EXPECT_NO_MODIFIERS_PRESSED();
}
*/
}
TEST_F(KeyEventHandlerTest, ProcessShiftModifiers) {
commands::KeyEvent key;
// 'Shift-a' senario
// Shift down
EXPECT_FALSE(ProcessKey(false, IBUS_Shift_L, &key));
EXPECT_TRUE(IsPressed(IBUS_Shift_L));
EXPECT_MODIFIERS_TO_BE_SENT(
(commands::KeyEvent::LEFT_SHIFT | commands::KeyEvent::SHIFT));
// "a" down
key.Clear();
EXPECT_TRUE(ProcessKey(false, 'a', &key));
EXPECT_FALSE(IsPressed(IBUS_Shift_L));
EXPECT_MODIFIERS_TO_BE_SENT(kNoModifiers);
// "a" up
key.Clear();
EXPECT_FALSE(ProcessKey(true, 'a', &key));
EXPECT_FALSE(IsPressed(IBUS_Shift_L));
EXPECT_MODIFIERS_TO_BE_SENT(kNoModifiers);
// Shift up
key.Clear();
EXPECT_FALSE(ProcessKey(true, IBUS_Shift_L, &key));
EXPECT_NO_MODIFIERS_PRESSED();
EXPECT_MODIFIERS_TO_BE_SENT(kNoModifiers);
/* Currently following test scenario does not pass.
* This bug was issued as b/4338394
// 'Shift-0' senario
// Shift down
EXPECT_FALSE(ProcessKey(false, IBUS_Shift_L, &key));
EXPECT_TRUE(IsPressed(IBUS_Shift_L));
EXPECT_MODIFIERS_TO_BE_SENT(
(commands::KeyEvent::LEFT_SHIFT | commands::KeyEvent::SHIFT));
// "0" down
key.Clear();
EXPECT_TRUE(ProcessKey(false, '0', &key));
EXPECT_TRUE(IsPressed(IBUS_Shift_L));
EXPECT_MODIFIERS_TO_BE_SENT(kNoModifiers);
EXPECT_EQ(modifiers_to_be_sent_.size(), 0);
// "0" up
key.Clear();
EXPECT_FALSE(ProcessKey(true, '0', &key));
EXPECT_TRUE(IsPressed(IBUS_Shift_L));
EXPECT_MODIFIERS_TO_BE_SENT(kNoModifiers);
// Shift up
key.Clear();
EXPECT_TRUE(ProcessKey(true, IBUS_Shift_L, &key));
EXPECT_NO_MODIFIERS_PRESSED();
EXPECT_MODIFIERS_TO_BE_SENT(kNoModifiers);
*/
}
TEST_F(KeyEventHandlerTest, ProcessAltModifiers) {
commands::KeyEvent key;
// Alt down
EXPECT_FALSE(ProcessKey(false, IBUS_Alt_L, &key));
EXPECT_TRUE(IsPressed(IBUS_Alt_L));
EXPECT_MODIFIERS_TO_BE_SENT(
(commands::KeyEvent::LEFT_ALT | commands::KeyEvent::ALT));
// "a" down
key.Clear();
key.add_modifier_keys(commands::KeyEvent::ALT);
key.add_modifier_keys(commands::KeyEvent::LEFT_ALT);
EXPECT_TRUE(ProcessKey(false, 'a', &key));
EXPECT_TRUE(IsPressed(IBUS_Alt_L));
EXPECT_MODIFIERS_TO_BE_SENT(kNoModifiers);
// "a" up
key.Clear();
key.add_modifier_keys(commands::KeyEvent::ALT);
key.add_modifier_keys(commands::KeyEvent::LEFT_ALT);
EXPECT_FALSE(ProcessKey(true, 'a', &key));
EXPECT_TRUE(IsPressed(IBUS_Alt_L));
EXPECT_MODIFIERS_TO_BE_SENT(kNoModifiers);
// ALt up
key.Clear();
EXPECT_FALSE(ProcessKey(true, IBUS_Alt_L, &key));
EXPECT_NO_MODIFIERS_PRESSED();
EXPECT_MODIFIERS_TO_BE_SENT(kNoModifiers);
}
TEST_F(KeyEventHandlerTest, ProcessCtrlModifiers) {
commands::KeyEvent key;
// Ctrl down
EXPECT_FALSE(ProcessKey(false, IBUS_Control_L, &key));
EXPECT_TRUE(IsPressed(IBUS_Control_L));
EXPECT_MODIFIERS_TO_BE_SENT(
(commands::KeyEvent::LEFT_CTRL | commands::KeyEvent::CTRL));
// "a" down
key.Clear();
key.add_modifier_keys(commands::KeyEvent::CTRL);
key.add_modifier_keys(commands::KeyEvent::LEFT_CTRL);
EXPECT_TRUE(ProcessKey(false, 'a', &key));
EXPECT_TRUE(IsPressed(IBUS_Control_L));
EXPECT_MODIFIERS_TO_BE_SENT(kNoModifiers);
// "a" up
key.Clear();
key.add_modifier_keys(commands::KeyEvent::CTRL);
key.add_modifier_keys(commands::KeyEvent::LEFT_CTRL);
EXPECT_FALSE(ProcessKey(true, 'a', &key));
EXPECT_TRUE(IsPressed(IBUS_Control_L));
EXPECT_MODIFIERS_TO_BE_SENT(kNoModifiers);
// Ctrl up
key.Clear();
EXPECT_FALSE(ProcessKey(true, IBUS_Control_L, &key));
EXPECT_NO_MODIFIERS_PRESSED();
EXPECT_MODIFIERS_TO_BE_SENT(kNoModifiers);
}
TEST_F(KeyEventHandlerTest, ProcessShiftModifiersWithCapsLockOn) {
commands::KeyEvent key;
// 'Shift-a' senario
// Shift down
EXPECT_FALSE(ProcessKeyWithCapsLock(false, IBUS_Shift_L, &key));
EXPECT_TRUE(IsPressed(IBUS_Shift_L));
EXPECT_MODIFIERS_TO_BE_SENT(
(commands::KeyEvent::CAPS | commands::KeyEvent::LEFT_SHIFT |
commands::KeyEvent::SHIFT));
// "a" down
key.Clear();
EXPECT_TRUE(ProcessKeyWithCapsLock(false, 'a', &key));
EXPECT_FALSE(IsPressed(IBUS_Shift_L));
EXPECT_MODIFIERS_TO_BE_SENT(kNoModifiers);
// "a" up
key.Clear();
EXPECT_FALSE(ProcessKeyWithCapsLock(true, 'a', &key));
EXPECT_FALSE(IsPressed(IBUS_Shift_L));
EXPECT_MODIFIERS_TO_BE_SENT(kNoModifiers);
// Shift up
key.Clear();
EXPECT_FALSE(ProcessKeyWithCapsLock(true, IBUS_Shift_L, &key));
EXPECT_NO_MODIFIERS_PRESSED();
EXPECT_MODIFIERS_TO_BE_SENT(kNoModifiers);
}
TEST_F(KeyEventHandlerTest, LeftRightModifiers) {
commands::KeyEvent key;
// Left-Shift down
EXPECT_FALSE(ProcessKey(false, IBUS_Shift_L, &key));
EXPECT_TRUE(IsPressed(IBUS_Shift_L));
EXPECT_MODIFIERS_TO_BE_SENT(
(commands::KeyEvent::LEFT_SHIFT | commands::KeyEvent::SHIFT));
// Right-Shift down
key.Clear();
key.add_modifier_keys(commands::KeyEvent::SHIFT);
key.add_modifier_keys(commands::KeyEvent::LEFT_SHIFT);
EXPECT_FALSE(ProcessKey(false, IBUS_Shift_R, &key));
EXPECT_TRUE(IsPressed(IBUS_Shift_L));
EXPECT_TRUE(IsPressed(IBUS_Shift_R));
EXPECT_MODIFIERS_TO_BE_SENT(
(commands::KeyEvent::LEFT_SHIFT | commands::KeyEvent::RIGHT_SHIFT |
commands::KeyEvent::SHIFT));
}
TEST_F(KeyEventHandlerTest, ProcessModifiers) {
commands::KeyEvent key;
// Shift down => Shift up
key.Clear();
ProcessKey(false, IBUS_Shift_L, &key);
key.Clear();
EXPECT_TRUE(ProcessKey(true, IBUS_Shift_L, &key));
EXPECT_NO_MODIFIERS_PRESSED();
EXPECT_MODIFIERS_TO_BE_SENT(kNoModifiers);
EXPECT_EQ((commands::KeyEvent::SHIFT | commands::KeyEvent::LEFT_SHIFT),
KeyEventUtil::GetModifiers(key));
// Shift down => Ctrl down => Shift up => Alt down => Ctrl up => Alt up
key.Clear();
ProcessKey(false, IBUS_Shift_L, &key);
key.Clear();
EXPECT_FALSE(ProcessKey(false, IBUS_Control_L, &key));
key.Clear();
EXPECT_FALSE(ProcessKey(true, IBUS_Shift_L, &key));
key.Clear();
EXPECT_FALSE(ProcessKey(false, IBUS_Alt_L, &key));
key.Clear();
EXPECT_FALSE(ProcessKey(true, IBUS_Control_L, &key));
key.Clear();
EXPECT_TRUE(ProcessKey(true, IBUS_Alt_L, &key));
EXPECT_NO_MODIFIERS_PRESSED();
EXPECT_MODIFIERS_TO_BE_SENT(kNoModifiers);
EXPECT_EQ((commands::KeyEvent::ALT | commands::KeyEvent::LEFT_ALT |
commands::KeyEvent::CTRL | commands::KeyEvent::LEFT_CTRL |
commands::KeyEvent::SHIFT | commands::KeyEvent::LEFT_SHIFT),
KeyEventUtil::GetModifiers(key));
}
TEST_F(KeyEventHandlerTest, ProcessModifiersRandomTest) {
// This test generates random key sequence and check that
// - All states are cleared when all keys are released.
// - All states are cleared when a non-modifier key with no modifier keys
// is pressed / released.
const guint kKeySet[] = {
IBUS_Alt_L,
IBUS_Alt_R,
IBUS_Control_L,
IBUS_Control_R,
IBUS_Shift_L,
IBUS_Shift_R,
IBUS_Caps_Lock,
IBUS_a,
};
const size_t kKeySetSize = arraysize(kKeySet);
Util::SetRandomSeed(static_cast<uint32>(Util::GetTime()));
const int kTrialNum = 1000;
for (int trial = 0; trial < kTrialNum; ++trial) {
handler_->Clear();
set<guint> pressed_keys;
string key_sequence;
const int kSequenceLength = 100;
for (int i = 0; i < kSequenceLength; ++i) {
const int key_index = Util::Random(kKeySetSize);
const guint key_value = kKeySet[key_index];
bool is_key_up;
if (pressed_keys.find(key_value) == pressed_keys.end()) {
pressed_keys.insert(key_value);
is_key_up = false;
} else {
pressed_keys.erase(key_value);
is_key_up = true;
}
key_sequence += Util::StringPrintf("is_key_up: %d, key_index = %d\n",
is_key_up, key_index);
commands::KeyEvent key;
for (set<guint>::const_iterator it = pressed_keys.begin();
it != pressed_keys.end(); ++it) {
AppendToKeyEvent(*it, &key);
}
ProcessKey(is_key_up, key_value, &key);
if (pressed_keys.empty()) {
SCOPED_TRACE("key_sequence:\n" + key_sequence);
EXPECT_FALSE(is_non_modifier_key_pressed());
EXPECT_NO_MODIFIERS_PRESSED();
EXPECT_MODIFIERS_TO_BE_SENT(kNoModifiers);
}
}
// Anytime non-modifier key without modifier key should clear states.
commands::KeyEvent key;
const guint non_modifier_key = IBUS_b;
AppendToKeyEvent(non_modifier_key, &key);
ProcessKey(false, non_modifier_key, &key);
{
const bool is_key_up = static_cast<bool>(Util::Random(2));
SCOPED_TRACE(Util::StringPrintf(
"Should be reset by non_modifier_key %s. key_sequence:\n%s",
(is_key_up ? "up" : "down"), key_sequence.c_str()));
EXPECT_FALSE(is_non_modifier_key_pressed());
EXPECT_NO_MODIFIERS_PRESSED();
EXPECT_MODIFIERS_TO_BE_SENT(kNoModifiers);
}
}
}
#undef EXPECT_MODIFIERS_TO_BE_SENT
#undef EXPECT_MODIFIERS_PRESSED
#undef EXPECT_NO_MODIFIERS_PRESSED
} // namespace ibus
} // namespace mozc