| // 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> ¤tly_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 |