blob: 758f5423568ffb1261a7e0a9bd9a451f7cda583a [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 "testing/base/public/googletest.h"
#include "testing/base/public/gunit.h"
#include "session/commands.pb.h"
#include "win32/base/deleter.h"
#include "win32/base/input_state.h"
#include "win32/base/keyboard.h"
namespace mozc {
namespace win32 {
namespace {
const BYTE kPressed = 0x80;
const uint64 kOutputId = 0x12345678;
class KeyboardMock : public Win32KeyboardInterface {
public:
KeyboardMock() {}
virtual bool IsKanaLocked(const KeyboardStatus &keyboard_state) {
return keyboard_state.IsPressed(VK_KANA);
}
virtual bool SetKeyboardState(const KeyboardStatus &keyboard_state) {
key_state_ = keyboard_state;
return true;
}
virtual bool GetKeyboardState(KeyboardStatus *keyboard_state) {
*keyboard_state = key_state_;
return true;
}
virtual bool AsyncIsKeyPressed(int virtual_key) {
return async_key_state_.IsPressed(virtual_key);
}
virtual int ToUnicode(UINT virtual_key, UINT scan_code,
const BYTE *key_state, LPWSTR unicode_buffer,
int unicode_buffer_num_elements, UINT flags) {
// We use a mock class in case the Japanese keyboard layout is not
// available on this system. This emulator class should work well in most
// cases. It returns an unicode character (if any) as if Japanese keyboard
// layout was currently active.
return JapaneseKeyboardLayoutEmulator::ToUnicode(
virtual_key, scan_code, key_state, unicode_buffer,
unicode_buffer_num_elements, flags);
}
virtual UINT SendInput(const vector<INPUT> &inputs) {
last_send_input_data_ = inputs;
return inputs.size();
}
const KeyboardStatus &key_state() const {
return key_state_;
}
void set_key_state(const KeyboardStatus &key_state) {
key_state_ = key_state;
}
const KeyboardStatus &async_key_state() const {
return async_key_state_;
}
void set_async_key_state(const KeyboardStatus &async_key_state) {
async_key_state_ = async_key_state;
}
const vector<INPUT> &last_send_input_data() const {
return last_send_input_data_;
}
void clear_last_send_input_data() {
last_send_input_data_.clear();
}
private:
KeyboardStatus key_state_;
KeyboardStatus async_key_state_;
vector<INPUT> last_send_input_data_;
DISALLOW_COPY_AND_ASSIGN(KeyboardMock);
};
} // namespace
TEST(VKBackBasedDeleterTest, OnKeyEventTestWhenNoDeletionIsOngoing) {
KeyboardMock *keyboard_mock = new KeyboardMock();
VKBackBasedDeleter deleter(keyboard_mock);
commands::Output output;
output.set_id(kOutputId);
InputState ime_state;
EXPECT_FALSE(deleter.IsDeletionOngoing());
EXPECT_EQ(0, keyboard_mock->last_send_input_data().size());
// OnKeyEvent never crashes even when there is no ongoing session.
EXPECT_EQ(VKBackBasedDeleter::DO_DEFAULT_ACTION,
deleter.OnKeyEvent(VK_BACK, true, true));
}
TEST(VKBackBasedDeleterTest, BeginDeletionTest_DeletionCountZero) {
KeyboardMock *keyboard_mock = new KeyboardMock();
VKBackBasedDeleter deleter(keyboard_mock);
commands::Output output;
output.set_id(kOutputId);
InputState ime_state;
// If the deletion count is zero, no deletion operation is started.
deleter.BeginDeletion(0, output, ime_state);
EXPECT_FALSE(deleter.IsDeletionOngoing());
EXPECT_EQ(0, keyboard_mock->last_send_input_data().size());
}
TEST(VKBackBasedDeleterTest, NormalSequence) {
const UINT kLastKey = 'A';
KeyboardMock *keyboard_mock = new KeyboardMock();
// VKBackBasedDeleter must clear any modifier before calling SendInput.
// To check this functionality, set VK_CONTROL bit into the mock.
// See b/3419452 for detailed information.
{
KeyboardStatus keyboard_state;
keyboard_state.SetState(VK_CONTROL, kPressed);
keyboard_mock->set_key_state(keyboard_state);
}
{
KeyboardStatus async_keyboard_state;
async_keyboard_state.SetState(VK_CONTROL, kPressed);
keyboard_mock->set_async_key_state(async_keyboard_state);
}
VKBackBasedDeleter deleter(keyboard_mock);
commands::Output output;
output.set_id(kOutputId);
InputState ime_state;
ime_state.last_down_key = VirtualKey::FromVirtualKey(kLastKey);
// Delete proceeding 3 characters.
deleter.BeginDeletion(3, output, ime_state);
EXPECT_FALSE(keyboard_mock->key_state().IsPressed(VK_CONTROL));
EXPECT_TRUE(keyboard_mock->async_key_state().IsPressed(VK_CONTROL));
EXPECT_TRUE(deleter.IsDeletionOngoing());
// Expect three pairs of VK_BACK [down/up] for deleting proceeding characters
// and one pair of VK_BACK [down/up] as a sentinel key event where pending
// output and ime state wiil be applied.
const vector<INPUT> inputs = keyboard_mock->last_send_input_data();
EXPECT_EQ(8, inputs.size());
EXPECT_EQ(VK_BACK, inputs[0].ki.wVk);
EXPECT_EQ(VK_BACK, inputs[1].ki.wVk);
EXPECT_EQ(VK_BACK, inputs[2].ki.wVk);
EXPECT_EQ(VK_BACK, inputs[3].ki.wVk);
EXPECT_EQ(VK_BACK, inputs[4].ki.wVk);
EXPECT_EQ(VK_BACK, inputs[5].ki.wVk);
EXPECT_EQ(VK_BACK, inputs[6].ki.wVk);
EXPECT_EQ(VK_BACK, inputs[7].ki.wVk);
EXPECT_EQ(0, inputs[0].ki.dwFlags);
EXPECT_EQ(KEYEVENTF_KEYUP, inputs[1].ki.dwFlags);
EXPECT_EQ(0, inputs[2].ki.dwFlags);
EXPECT_EQ(KEYEVENTF_KEYUP, inputs[3].ki.dwFlags);
EXPECT_EQ(0, inputs[4].ki.dwFlags);
EXPECT_EQ(KEYEVENTF_KEYUP, inputs[5].ki.dwFlags);
EXPECT_EQ(0, inputs[6].ki.dwFlags);
EXPECT_EQ(KEYEVENTF_KEYUP, inputs[7].ki.dwFlags);
// Initially, the deleter is waiting for the first VK_BACK test-key-down.
EXPECT_EQ(VKBackBasedDeleter::DO_DEFAULT_ACTION,
deleter.OnKeyEvent(VK_TAB, true, true));
EXPECT_EQ(VKBackBasedDeleter::DO_DEFAULT_ACTION,
deleter.OnKeyEvent(VK_TAB, false, true));
EXPECT_EQ(VKBackBasedDeleter::DO_DEFAULT_ACTION,
deleter.OnKeyEvent('X', true, true));
EXPECT_EQ(VKBackBasedDeleter::DO_DEFAULT_ACTION,
deleter.OnKeyEvent('X', false, true));
// The first pair of test-key-down/up.
EXPECT_EQ(VKBackBasedDeleter::SEND_KEY_TO_APPLICATION,
deleter.OnKeyEvent(VK_BACK, true, true));
EXPECT_EQ(VKBackBasedDeleter::SEND_KEY_TO_APPLICATION,
deleter.OnKeyEvent(VK_BACK, false, true));
// The second pair of test-key-down/up.
EXPECT_EQ(VKBackBasedDeleter::SEND_KEY_TO_APPLICATION,
deleter.OnKeyEvent(VK_BACK, true, true));
EXPECT_EQ(VKBackBasedDeleter::SEND_KEY_TO_APPLICATION,
deleter.OnKeyEvent(VK_BACK, false, true));
// The third pair of test-key-down/up.
EXPECT_EQ(VKBackBasedDeleter::SEND_KEY_TO_APPLICATION,
deleter.OnKeyEvent(VK_BACK, true, true));
EXPECT_EQ(VKBackBasedDeleter::SEND_KEY_TO_APPLICATION,
deleter.OnKeyEvent(VK_BACK, false, true));
// The last key-down will not be sent to the application.
EXPECT_EQ(VKBackBasedDeleter::CONSUME_KEY_BUT_NEVER_SEND_TO_SERVER,
deleter.OnKeyEvent(VK_BACK, true, true));
EXPECT_EQ(VKBackBasedDeleter::APPLY_PENDING_STATUS,
deleter.OnKeyEvent(VK_BACK, true, false));
// Check the pending output and state.
EXPECT_EQ(kOutputId, deleter.pending_output().id());
EXPECT_EQ(kLastKey, deleter.pending_ime_state().last_down_key.virtual_key());
// The last key-up will not be sent to the application.
EXPECT_EQ(VKBackBasedDeleter::CONSUME_KEY_BUT_NEVER_SEND_TO_SERVER,
deleter.OnKeyEvent(VK_BACK, false, true));
EXPECT_EQ(VKBackBasedDeleter::CALL_END_DELETION_BUT_NEVER_SEND_TO_SERVER,
deleter.OnKeyEvent(VK_BACK, false, false));
// Make sure the status of modifier keys have not changed.
EXPECT_FALSE(keyboard_mock->key_state().IsPressed(VK_CONTROL));
EXPECT_TRUE(keyboard_mock->async_key_state().IsPressed(VK_CONTROL));
// The caller must call EndDeletion when
// CALL_END_DELETION_BUT_NEVER_SEND_TO_SERVER is returned.
deleter.EndDeletion();
// After EndDeletion, the modifier state should be restored based on the
// async key state.
EXPECT_TRUE(keyboard_mock->key_state().IsPressed(VK_CONTROL));
EXPECT_TRUE(keyboard_mock->async_key_state().IsPressed(VK_CONTROL));
// Internal status must be reset by VKBackBasedDeleter::Reset().
EXPECT_FALSE(deleter.IsDeletionOngoing());
}
TEST(VKBackBasedDeleterTest, BeginDeletion_InsuccessfulCase) {
const UINT kLastKey = 'A';
KeyboardMock *keyboard_mock = new KeyboardMock();
// VKBackBasedDeleter must clear any modifier before calling SendInput.
// To check this functionality, set VK_CONTROL bit into the mock.
// See b/3419452 for detailed information.
{
KeyboardStatus keyboard_state;
keyboard_state.SetState(VK_CONTROL, kPressed);
keyboard_mock->set_key_state(keyboard_state);
}
{
KeyboardStatus async_keyboard_state;
async_keyboard_state.SetState(VK_CONTROL, kPressed);
keyboard_mock->set_async_key_state(async_keyboard_state);
}
VKBackBasedDeleter deleter(keyboard_mock);
commands::Output output;
output.set_id(kOutputId);
InputState ime_state;
ime_state.last_down_key = VirtualKey::FromVirtualKey(kLastKey);
// Delete proceeding 3 characters.
deleter.BeginDeletion(3, output, ime_state);
EXPECT_FALSE(keyboard_mock->key_state().IsPressed(VK_CONTROL));
EXPECT_TRUE(keyboard_mock->async_key_state().IsPressed(VK_CONTROL));
EXPECT_TRUE(deleter.IsDeletionOngoing());
// The first pair of test-key-down/up.
EXPECT_EQ(VKBackBasedDeleter::SEND_KEY_TO_APPLICATION,
deleter.OnKeyEvent(VK_BACK, true, true));
EXPECT_EQ(VKBackBasedDeleter::SEND_KEY_TO_APPLICATION,
deleter.OnKeyEvent(VK_BACK, false, true));
// If an unexpected key is passed, the deletion sequence must be terminated.
EXPECT_EQ(VKBackBasedDeleter::CALL_END_DELETION_THEN_DO_DEFAULT_ACTION,
deleter.OnKeyEvent(VK_TAB, true, true));
// Make sure the status of modifier keys have not changed.
EXPECT_FALSE(keyboard_mock->key_state().IsPressed(VK_CONTROL));
EXPECT_TRUE(keyboard_mock->async_key_state().IsPressed(VK_CONTROL));
// The caller must call EndDeletion when
// CALL_END_DELETION_THEN_DO_DEFAULT_ACTION is returned.
deleter.EndDeletion();
// After EndDeletion, the modifier state should be restored based on the
// async key state.
EXPECT_TRUE(keyboard_mock->key_state().IsPressed(VK_CONTROL));
EXPECT_TRUE(keyboard_mock->async_key_state().IsPressed(VK_CONTROL));
// Internal status must be reset by VKBackBasedDeleter::Reset().
EXPECT_FALSE(deleter.IsDeletionOngoing());
}
} // namespace win32
} // namespace mozc