mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-04 16:05:31 +00:00
chore(linux): Add integration tests for ibus-keyman
This change adds integration/regression tests for ibus-keyman. It re-uses the unit tests that we have for core. Also modifies one test so that the context doesn't use characters used in the keyboard. This makes it easier to setup in ibus-keyman tests.
This commit is contained in:
parent
db5b9db23b
commit
cdee19e953
14 changed files with 2179 additions and 9 deletions
1
.flake8
1
.flake8
|
|
@ -1,5 +1,6 @@
|
|||
[flake8]
|
||||
max-line-length = 120
|
||||
indent-size = 2
|
||||
ignore =
|
||||
E402, # module level import not at top of file
|
||||
W504, # line break after binary operator
|
||||
|
|
|
|||
6
common/core/desktop/.gitignore
vendored
6
common/core/desktop/.gitignore
vendored
|
|
@ -11,3 +11,9 @@ VERSION.md
|
|||
shellHelperFunctions.sh
|
||||
|
||||
src/rust/keyman_keyboard_processor/target/
|
||||
|
||||
# The following files/subdirs are generated by autotools when building ibus-keyman.
|
||||
# They can be removed after we convert ibus-keyman to use meson.
|
||||
.deps/
|
||||
.dirstamp
|
||||
*.o
|
||||
|
|
|
|||
|
|
@ -6,14 +6,14 @@ c 4. Two deadkeys in a row in context dk(4a) dk(4b) + BKSP = nul
|
|||
c 5. One char and two deadkeys in context 'a' dk(5a) dk(5b) 'b' + BKSP = 'a'
|
||||
c 6. One char and two deadkeys and one char and two deadkeys in context 'a' dk(6a) dk(6b) 'b' dk(6c) dk(6d) + BKSP = 'a'
|
||||
c keys: [K_1][K_BKSP][K_2][K_BKSP][K_3][K_BKSP][K_4][K_BKSP][K_5][K_BKSP][K_6][K_BKSP]
|
||||
c expected: 12aa
|
||||
c context: 1234
|
||||
c expected: 78aa
|
||||
c context: 7890
|
||||
|
||||
store(&VERSION) '9.0'
|
||||
|
||||
begin unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
group(main) using keys
|
||||
|
||||
+ '1' > dk(1)
|
||||
+ '2' > 'a' dk(2)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
SUBDIRS = \
|
||||
src \
|
||||
tests \
|
||||
icons \
|
||||
m4 \
|
||||
po \
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ Makefile
|
|||
src/Makefile
|
||||
src/keyman.xml.in
|
||||
src/keyman-version.h
|
||||
tests/Makefile
|
||||
icons/Makefile
|
||||
m4/Makefile
|
||||
])
|
||||
|
|
|
|||
|
|
@ -162,13 +162,13 @@ static void
|
|||
ibus_keyman_engine_class_init (IBusKeymanEngineClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass);
|
||||
IBusEngineClass *engine_class = IBUS_ENGINE_CLASS (klass);
|
||||
IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass);
|
||||
IBusEngineClass *engine_class = IBUS_ENGINE_CLASS (klass);
|
||||
|
||||
parent_class = (IBusEngineClass *) g_type_class_peek_parent (klass);
|
||||
parent_class = (IBusEngineClass *) g_type_class_peek_parent (klass);
|
||||
|
||||
object_class->constructor = ibus_keyman_engine_constructor;
|
||||
ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_keyman_engine_destroy;
|
||||
ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_keyman_engine_destroy;
|
||||
|
||||
engine_class->process_key_event = ibus_keyman_engine_process_key_event;
|
||||
|
||||
|
|
@ -586,8 +586,7 @@ process_backspace_action(
|
|||
return FALSE;
|
||||
} else {
|
||||
g_message(
|
||||
"DAR: ibus_keyman_engine_process_key_event - client_capabilities=%x, %x", engine->client_capabilities,
|
||||
IBUS_CAP_SURROUNDING_TEXT);
|
||||
"DAR: process_backspace_action - client_capabilities=%x, %x", engine->client_capabilities, IBUS_CAP_SURROUNDING_TEXT);
|
||||
|
||||
if ((engine->client_capabilities & IBUS_CAP_SURROUNDING_TEXT) != 0) {
|
||||
g_message("deleting surrounding text 1 char");
|
||||
|
|
|
|||
2
linux/ibus-keyman/tests/.gitignore
vendored
Normal file
2
linux/ibus-keyman/tests/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
.libs/
|
||||
ibus-keyman-tests
|
||||
112
linux/ibus-keyman/tests/Makefile.am
Normal file
112
linux/ibus-keyman/tests/Makefile.am
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
# vim:set noet ts=4:
|
||||
#
|
||||
# ibus-keyman unit tests
|
||||
#
|
||||
# Copyright (c) 2021 SIL International
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
AUTOMAKE_OPTIONS = subdir-objects
|
||||
|
||||
AM_CFLAGS = \
|
||||
@IBUS_CFLAGS@ \
|
||||
@GTK_CFLAGS@ \
|
||||
@JSON_GLIB_CFLAGS@ \
|
||||
@X11_CFLAGS@ \
|
||||
@KEYMAN_PROC_CFLAGS@ \
|
||||
-DPKGDATADIR=\"$(pkgdatadir)\" \
|
||||
$(NULL)
|
||||
|
||||
AM_LDFLAGS = \
|
||||
@IBUS_LIBS@ \
|
||||
@GTK_LIBS@ \
|
||||
@X11_LIBS@ \
|
||||
@JSON_GLIB_LIBS@ \
|
||||
@KEYMAN_PROC_LIBS@ \
|
||||
$(NULL)
|
||||
|
||||
libexec_PROGRAMS = ibus-keyman-tests
|
||||
|
||||
ibus_keyman_tests_SOURCES = \
|
||||
ibusimcontext.c \
|
||||
ibusimcontext.h \
|
||||
testfixture.cpp \
|
||||
testmodule.c \
|
||||
testmodule.h \
|
||||
../../../common/core/desktop/src/json.cpp \
|
||||
../../../common/core/desktop/src/keyboard.cpp \
|
||||
../../../common/core/desktop/src/kmx/kmx_actions.cpp \
|
||||
../../../common/core/desktop/src/kmx/kmx_actions.h \
|
||||
../../../common/core/desktop/src/kmx/kmx_base.h \
|
||||
../../../common/core/desktop/src/kmx/kmx_capslock.cpp \
|
||||
../../../common/core/desktop/src/kmx/kmx_consts.cpp \
|
||||
../../../common/core/desktop/src/kmx/kmx_context.cpp \
|
||||
../../../common/core/desktop/src/kmx/kmx_context.h \
|
||||
../../../common/core/desktop/src/kmx/kmx_debug.cpp \
|
||||
../../../common/core/desktop/src/kmx/kmx_debugger.cpp \
|
||||
../../../common/core/desktop/src/kmx/kmx_debugger.h \
|
||||
../../../common/core/desktop/src/kmx/kmx_environment.cpp \
|
||||
../../../common/core/desktop/src/kmx/kmx_environment.h \
|
||||
../../../common/core/desktop/src/kmx/kmx_file.cpp \
|
||||
../../../common/core/desktop/src/kmx/kmx_file.h \
|
||||
../../../common/core/desktop/src/kmx/kmx_modifiers.cpp \
|
||||
../../../common/core/desktop/src/kmx/kmx_options.cpp \
|
||||
../../../common/core/desktop/src/kmx/kmx_options.h \
|
||||
../../../common/core/desktop/src/kmx/kmx_processevent.cpp \
|
||||
../../../common/core/desktop/src/kmx/kmx_processevent.h \
|
||||
../../../common/core/desktop/src/kmx/kmx_processor.cpp \
|
||||
../../../common/core/desktop/src/kmx/kmx_processor.hpp \
|
||||
../../../common/core/desktop/src/kmx/kmx_xstring.cpp \
|
||||
../../../common/core/desktop/src/kmx/kmx_xstring.h \
|
||||
../../../common/core/desktop/src/option.cpp \
|
||||
../../../common/core/desktop/src/state.cpp \
|
||||
../../../common/core/desktop/src/utfcodec.cpp \
|
||||
../../../common/core/desktop/tests/unit/kmx/kmx_test_source.cpp \
|
||||
../../../common/core/desktop/tests/unit/kmx/kmx_test_source.hpp \
|
||||
$(NULL)
|
||||
|
||||
ibus_keyman_tests_CFLAGS = \
|
||||
@IBUS_CFLAGS@ \
|
||||
@GTK_CFLAGS@ \
|
||||
@X11_CFLAGS@ \
|
||||
@JSON_GLIB_CFLAGS@ \
|
||||
@KEYMAN_PROC_CFLAGS@ \
|
||||
-DPKGDATADIR=\"$(pkgdatadir)\" \
|
||||
-I../../../common/core/desktop/src \
|
||||
-I../../../common/core/desktop/src/kmx \
|
||||
-I../../../common/core/desktop/tests/unit/kmx \
|
||||
-I../src \
|
||||
$(NULL)
|
||||
|
||||
ibus_keyman_tests_CPPFLAGS = \
|
||||
@IBUS_CFLAGS@ \
|
||||
@GTK_CFLAGS@ \
|
||||
@X11_CFLAGS@ \
|
||||
@JSON_GLIB_CFLAGS@ \
|
||||
@KEYMAN_PROC_CFLAGS@ \
|
||||
-DPKGDATADIR=\"$(pkgdatadir)\" \
|
||||
-I../../../common/core/desktop/src \
|
||||
-I../../../common/core/desktop/src/kmx \
|
||||
-I../../../common/core/desktop/tests/unit/kmx \
|
||||
-I../src \
|
||||
$(NULL)
|
||||
|
||||
ibus_keyman_tests_LDFLAGS = \
|
||||
@GTK_LIBS@ \
|
||||
@IBUS_LIBS@ \
|
||||
@X11_LIBS@ \
|
||||
@JSON_GLIB_LIBS@ \
|
||||
@KEYMAN_PROC_LIBS@ \
|
||||
$(NULL)
|
||||
1487
linux/ibus-keyman/tests/ibusimcontext.c
Normal file
1487
linux/ibus-keyman/tests/ibusimcontext.c
Normal file
File diff suppressed because it is too large
Load diff
83
linux/ibus-keyman/tests/ibusimcontext.h
Normal file
83
linux/ibus-keyman/tests/ibusimcontext.h
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
|
||||
/* vim:set et ts=4: */
|
||||
/* ibus - The Input Bus
|
||||
* Copyright (C) 2008-2010 Peng Huang <shawn.p.huang@gmail.com>
|
||||
* Copyright (C) 2008-2010 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
#ifndef __IBUSIMCONTEXT_H__
|
||||
#define __IBUSIMCONTEXT_H__
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
/*
|
||||
* Type macros.
|
||||
*/
|
||||
#define IBUS_TYPE_IM_CONTEXT \
|
||||
(ibus_im_context_get_type ())
|
||||
#define IBUS_IM_CONTEXT(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST ((obj), IBUS_TYPE_IM_CONTEXT, IBusIMContext))
|
||||
#define IBUS_IM_CONTEXT_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST ((klass), IBUS_TYPE_IM_CONTEXT, IBusIMContextClass))
|
||||
#define IBUS_IS_IM_CONTEXT(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), IBUS_TYPE_IM_CONTEXT))
|
||||
#define IBUS_IS_IM_CONTEXT_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE ((klass), IBUS_TYPE_IM_CONTEXT))
|
||||
#define IBUS_IM_CONTEXT_GET_CLASS(obj) \
|
||||
(G_TYPE_CHECK_GET_CLASS ((obj), IBUS_TYPE_IM_CONTEXT, IBusIMContextClass))
|
||||
|
||||
G_BEGIN_DECLS
|
||||
typedef struct _IBusIMContext IBusIMContext;
|
||||
typedef struct _IBusIMContextClass IBusIMContextClass;
|
||||
typedef struct _IBusIMContextPrivate IBusIMContextPrivate;
|
||||
|
||||
|
||||
GType ibus_im_context_get_type (void);
|
||||
IBusIMContext
|
||||
*ibus_im_context_new (void);
|
||||
void ibus_im_context_register_type
|
||||
(GTypeModule *type_module);
|
||||
void ibus_im_context_shutdown
|
||||
(void);
|
||||
const gchar
|
||||
*ibus_im_context_get_ic (IBusIMContext *context);
|
||||
void ibus_im_context_set_ic (IBusIMContext *context,
|
||||
const gchar *ic);
|
||||
void ibus_im_context_enable (IBusIMContext *context);
|
||||
void ibus_im_context_disable (IBusIMContext *context);
|
||||
void ibus_im_context_commit_string
|
||||
(IBusIMContext *context,
|
||||
const gchar *string);
|
||||
void ibus_im_context_update_preedit
|
||||
(IBusIMContext *context,
|
||||
const gchar *string,
|
||||
PangoAttrList *attrs,
|
||||
gint cursor_pos,
|
||||
gboolean visible);
|
||||
void ibus_im_context_show_preedit
|
||||
(IBusIMContext *context);
|
||||
void ibus_im_context_hide_preedit
|
||||
(IBusIMContext *context);
|
||||
|
||||
void ibus_im_test_set_thread_loop(IBusIMContext *context, GMainLoop *loop);
|
||||
|
||||
void ibus_im_test_set_text(IBusIMContext *context, const gchar *text);
|
||||
const gchar *ibus_im_test_get_text(IBusIMContext *context);
|
||||
void ibus_im_test_clear_text(IBusIMContext *context);
|
||||
G_END_DECLS
|
||||
#endif // __IBUSIMCONTEXT_H__
|
||||
|
||||
29
linux/ibus-keyman/tests/run-tests.sh
Executable file
29
linux/ibus-keyman/tests/run-tests.sh
Executable file
|
|
@ -0,0 +1,29 @@
|
|||
#!/bin/bash
|
||||
|
||||
BASEDIR=$(dirname $0)
|
||||
TESTDIR=${XDG_DATA_HOME:-$HOME/.local/share}/keyman/test_kmx
|
||||
|
||||
if [ "$DISPLAY" == ":0" ]; then
|
||||
echo "Please start xephyr and set the DISPLAY environment variable before running these tests."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d $TESTDIR ]; then
|
||||
ln -sf $BASEDIR/../../../common/core/desktop/build/arch/debug/tests/unit/kmx $TESTDIR
|
||||
fi
|
||||
|
||||
if [ $# -gt 0 ]; then
|
||||
TESTFILES=$@
|
||||
else
|
||||
pushd $TESTDIR
|
||||
TESTFILES=(*.kmx)
|
||||
popd
|
||||
fi
|
||||
|
||||
export GSETTINGS_BACKEND=keyfile
|
||||
|
||||
ibus-daemon --panel=disable --config=/usr/libexec/ibus-memconf > /tmp/ibus-daemon.log 2>&1 &
|
||||
../src/ibus-engine-keyman > /tmp/ibus-engine-keyman.log 2>&1 &
|
||||
sleep 1s
|
||||
|
||||
./ibus-keyman-tests -k --tap --directory $TESTDIR "${TESTFILES[@]}"
|
||||
352
linux/ibus-keyman/tests/testfixture.cpp
Normal file
352
linux/ibus-keyman/tests/testfixture.cpp
Normal file
|
|
@ -0,0 +1,352 @@
|
|||
#include <glib-object.h>
|
||||
#include <glib.h>
|
||||
#include <ibus.h>
|
||||
#include <iostream>
|
||||
#include <keyman/keyboardprocessor.h>
|
||||
#include <kmx/kmx_processevent.h>
|
||||
#include <linux/input-event-codes.h>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <stack>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include "ibusimcontext.h"
|
||||
#include "keycodes.h"
|
||||
#include "kmx_test_source.hpp"
|
||||
#include "testmodule.h"
|
||||
|
||||
typedef struct {
|
||||
IBusBus *bus;
|
||||
GtkIMContext *context;
|
||||
IBusIMContext *ibuscontext;
|
||||
} IBusKeymanTestsFixture;
|
||||
|
||||
static gboolean loaded = FALSE;
|
||||
static GdkWindow *window = NULL;
|
||||
static GMainLoop *thread_loop = NULL;
|
||||
static GTypeModule *module = NULL;
|
||||
|
||||
static void
|
||||
module_register(GTypeModule *module) {
|
||||
ibus_im_context_register_type(module);
|
||||
}
|
||||
|
||||
void
|
||||
destroy(GtkWidget *widget, gpointer data) {
|
||||
gtk_main_quit();
|
||||
}
|
||||
|
||||
static void
|
||||
ibus_keyman_tests_fixture_set_up(IBusKeymanTestsFixture *fixture, gconstpointer user_data) {
|
||||
IBusIMContextClass *classClass;
|
||||
|
||||
if (!window) {
|
||||
GtkWidget *widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
g_signal_connect(widget, "destroy", G_CALLBACK(destroy), NULL);
|
||||
window = gtk_widget_get_window(widget);
|
||||
}
|
||||
|
||||
if (!loaded) {
|
||||
ibus_init();
|
||||
|
||||
module = test_module_new(module_register);
|
||||
g_assert_nonnull(module);
|
||||
|
||||
/* Not loaded until we call ref for the first time */
|
||||
classClass = static_cast<IBusIMContextClass *>(g_type_class_peek(IBUS_TYPE_IM_CONTEXT));
|
||||
g_assert_null(classClass);
|
||||
|
||||
loaded = TRUE;
|
||||
}
|
||||
|
||||
fixture->bus = ibus_bus_new();
|
||||
}
|
||||
|
||||
static void
|
||||
ibus_keyman_tests_fixture_tear_down(IBusKeymanTestsFixture *fixture, gconstpointer user_data) {
|
||||
if (thread_loop) {
|
||||
g_main_loop_unref(thread_loop);
|
||||
thread_loop = NULL;
|
||||
}
|
||||
|
||||
g_clear_object(&fixture->bus);
|
||||
g_clear_object(&fixture->context);
|
||||
}
|
||||
|
||||
static void
|
||||
switch_keyboard(IBusKeymanTestsFixture *fixture, const gchar *keyboard) {
|
||||
ibus_bus_set_global_engine(fixture->bus, keyboard);
|
||||
|
||||
IBusEngineDesc *desc = ibus_bus_get_global_engine(fixture->bus);
|
||||
g_object_ref_sink(desc);
|
||||
// g_debug("Switched to engine: '%s'", ibus_engine_desc_get_name(desc));
|
||||
g_assert_cmpstr(keyboard, ==, ibus_engine_desc_get_name(desc));
|
||||
g_clear_object(&desc);
|
||||
|
||||
if (thread_loop) {
|
||||
g_main_loop_unref(thread_loop);
|
||||
thread_loop = NULL;
|
||||
}
|
||||
if (fixture->context) {
|
||||
g_clear_object(&fixture->context);
|
||||
}
|
||||
fixture->ibuscontext = ibus_im_context_new();
|
||||
fixture->context = (GtkIMContext *)fixture->ibuscontext;
|
||||
|
||||
thread_loop = g_main_loop_new(NULL, TRUE);
|
||||
ibus_im_test_set_thread_loop(fixture->ibuscontext, thread_loop);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
std::string
|
||||
string_format(const std::string &format, Args... args) {
|
||||
// from https://stackoverflow.com/a/26221725/487503
|
||||
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1; // Extra space for '\0'
|
||||
if (size_s <= 0) {
|
||||
throw std::runtime_error("Error during formatting.");
|
||||
}
|
||||
auto size = static_cast<size_t>(size_s);
|
||||
auto buf = std::make_unique<char[]>(size);
|
||||
std::snprintf(buf.get(), size, format.c_str(), args...);
|
||||
return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
|
||||
}
|
||||
|
||||
static unsigned short vk_to_keycode(unsigned short vk) {
|
||||
for (int i = 0; i < sizeof(keycode_to_vk); i++) {
|
||||
if (keycode_to_vk[i] == vk)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define KEYMAN_LCTRL 29 // 0x1D
|
||||
#define KEYMAN_LALT 56 // 0x38
|
||||
#define KEYMAN_RCTRL 97 // 0x61
|
||||
#define KEYMAN_RALT 100 // 0x64
|
||||
|
||||
static guint
|
||||
km_modifiers_to_ibus(km::tests::KmxTestSource& test_source, km::tests::key_event event) {
|
||||
guint result = 0;
|
||||
auto state = event.modifier_state | test_source.caps_lock_state();
|
||||
if (state & KM_KBP_MODIFIER_SHIFT) {
|
||||
result |= IBUS_SHIFT_MASK;
|
||||
}
|
||||
if (state & KM_KBP_MODIFIER_ALT) {
|
||||
result |= IBUS_MOD1_MASK;
|
||||
}
|
||||
if (state & KM_KBP_MODIFIER_RALT) {
|
||||
if (event.vk == KEYMAN_RALT)
|
||||
result |= IBUS_MOD1_MASK;
|
||||
else
|
||||
result |= IBUS_MOD5_MASK;
|
||||
}
|
||||
if (state & KM_KBP_MODIFIER_LALT) {
|
||||
result |= IBUS_MOD1_MASK;
|
||||
}
|
||||
if (state & KM_KBP_MODIFIER_CTRL) {
|
||||
result |= IBUS_CONTROL_MASK;
|
||||
}
|
||||
if (state & KM_KBP_MODIFIER_RCTRL) {
|
||||
result |= IBUS_CONTROL_MASK;
|
||||
}
|
||||
if (state & KM_KBP_MODIFIER_LCTRL) {
|
||||
result |= IBUS_CONTROL_MASK;
|
||||
}
|
||||
if (state & KM_KBP_MODIFIER_CAPS) {
|
||||
result |= IBUS_LOCK_MASK;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
guint modifiers_keydown;
|
||||
guint modifiers_keyup;
|
||||
guint keyval;
|
||||
guint16 keycode;
|
||||
guint is_modifier;
|
||||
} gdk_key_event;
|
||||
|
||||
static gdk_key_event
|
||||
get_key_event_for_modifier(guint modifier, guint & prev_modifiers, guint keyval, guint keycode) {
|
||||
gdk_key_event event;
|
||||
event.modifiers_keydown = prev_modifiers;
|
||||
event.modifiers_keyup = prev_modifiers | modifier | IBUS_RELEASE_MASK;
|
||||
prev_modifiers |= modifier;
|
||||
event.keyval = keyval;
|
||||
event.keycode = keycode;
|
||||
event.is_modifier = 1;
|
||||
return event;
|
||||
}
|
||||
|
||||
static std::list<gdk_key_event>
|
||||
get_involved_keys(km::tests::KmxTestSource &test_source, km::tests::key_event test_event) {
|
||||
guint modifiers = 0;
|
||||
if (test_source.caps_lock_state()) {
|
||||
modifiers = IBUS_LOCK_MASK;
|
||||
}
|
||||
|
||||
// process all modifiers
|
||||
std::list<gdk_key_event> result;
|
||||
if (test_event.modifier_state & KM_KBP_MODIFIER_SHIFT) {
|
||||
// we don't distinguish between R and L Shift
|
||||
result.push_back(get_key_event_for_modifier(IBUS_SHIFT_MASK, modifiers, GDK_KEY_Shift_L, KEY_LEFTSHIFT));
|
||||
}
|
||||
if (test_event.modifier_state & KM_KBP_MODIFIER_ALT || test_event.modifier_state & KM_KBP_MODIFIER_LALT) {
|
||||
result.push_back(get_key_event_for_modifier(IBUS_MOD1_MASK, modifiers, GDK_KEY_Alt_L, KEY_LEFTALT));
|
||||
}
|
||||
if (test_event.modifier_state & KM_KBP_MODIFIER_RALT) {
|
||||
if (test_event.vk == KEYMAN_RALT)
|
||||
result.push_back(get_key_event_for_modifier(IBUS_MOD1_MASK, modifiers, GDK_KEY_Alt_R, KEY_RIGHTALT));
|
||||
else
|
||||
result.push_back(get_key_event_for_modifier(IBUS_MOD5_MASK, modifiers, GDK_KEY_Alt_R, KEY_RIGHTALT));
|
||||
}
|
||||
if (test_event.modifier_state & KM_KBP_MODIFIER_CTRL || test_event.modifier_state & KM_KBP_MODIFIER_LCTRL) {
|
||||
result.push_back(get_key_event_for_modifier(IBUS_CONTROL_MASK, modifiers, GDK_KEY_Control_L, KEY_LEFTCTRL));
|
||||
}
|
||||
if (test_event.modifier_state & KM_KBP_MODIFIER_RCTRL) {
|
||||
result.push_back(get_key_event_for_modifier(IBUS_CONTROL_MASK, modifiers, GDK_KEY_Control_R, KEY_RIGHTCTRL));
|
||||
}
|
||||
|
||||
// process key
|
||||
guint keyval = (test_event.modifier_state & KM_KBP_MODIFIER_SHIFT) ? gdk_keyval_to_upper(test_event.vk)
|
||||
: gdk_keyval_to_lower(test_event.vk);
|
||||
gdk_key_event event;
|
||||
event.modifiers_keydown = modifiers;
|
||||
event.modifiers_keyup = modifiers | IBUS_RELEASE_MASK;
|
||||
event.keyval = keyval;
|
||||
event.keycode = vk_to_keycode(test_event.vk);
|
||||
event.is_modifier = 0;
|
||||
result.push_back(event);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::list<km::tests::key_event>
|
||||
get_context_keys(std::u16string context) {
|
||||
std::list<km::tests::key_event> result;
|
||||
for (auto c = context.begin(); c != context.end(); c++) {
|
||||
gchar utf8[6];
|
||||
if (g_unichar_to_utf8((gunichar)*c, utf8) != 1 || utf8[0] < 32 || utf8[0] > 127) {
|
||||
g_assertion_message_cmpstr(
|
||||
G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, "We can only deal with ASCII characters in the context", utf8, "", "");
|
||||
}
|
||||
result.push_back(
|
||||
{km::kbp::kmx::s_char_to_vkey[(int)utf8[0] - 32].vk,
|
||||
(uint16_t)(km::kbp::kmx::s_char_to_vkey[(int)utf8[0] - 32].shifted ? KM_KBP_MODIFIER_SHIFT : 0)});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
press_keys(IBusKeymanTestsFixture *fixture, km::tests::KmxTestSource & test_source,km::tests::key_event key_event) {
|
||||
auto involved_keys = get_involved_keys(test_source, key_event);
|
||||
|
||||
// press and hold the individual modifier keys, finally the actual key
|
||||
std::stack<GdkEventKey> keys_to_release;
|
||||
for (auto key = involved_keys.begin(); key != involved_keys.end(); key++) {
|
||||
GdkEventKey keyEvent = {
|
||||
.type = GDK_KEY_PRESS,
|
||||
.window = window,
|
||||
.send_event = 0,
|
||||
.time = 0,
|
||||
.state = key->modifiers_keydown,
|
||||
.keyval = key->keyval,
|
||||
.length = 0,
|
||||
.string = NULL,
|
||||
.hardware_keycode = key->keycode,
|
||||
.group = 0,
|
||||
.is_modifier = key->is_modifier};
|
||||
gtk_im_context_filter_keypress(fixture->context, &keyEvent);
|
||||
|
||||
keyEvent.type = GDK_KEY_RELEASE;
|
||||
keyEvent.state = key->modifiers_keyup;
|
||||
keys_to_release.push(keyEvent);
|
||||
}
|
||||
|
||||
// then release the keys in reverse order
|
||||
while (!keys_to_release.empty()) {
|
||||
auto keyEvent = keys_to_release.top();
|
||||
gtk_im_context_filter_keypress(fixture->context, &keyEvent);
|
||||
keys_to_release.pop();
|
||||
}
|
||||
}
|
||||
|
||||
static void test_source(IBusKeymanTestsFixture *fixture, gconstpointer user_data) {
|
||||
auto sourcefile = string_format("%s.kmn", (char *)user_data);
|
||||
auto kmxfile = string_format("und:%s.kmx", (char*)user_data);
|
||||
|
||||
km::tests::KmxTestSource test_source;
|
||||
std::string keys = "";
|
||||
std::u16string expected = u"", context = u"";
|
||||
km::tests::kmx_options options;
|
||||
bool expected_beep = false;
|
||||
g_assert_cmpint(test_source.load_source(sourcefile.c_str(), keys, expected, context, options, expected_beep), ==, 0);
|
||||
|
||||
switch_keyboard(fixture, kmxfile.c_str());
|
||||
|
||||
auto contextKeys = get_context_keys(context);
|
||||
for (auto k = contextKeys.begin(); k != contextKeys.end(); k++) {
|
||||
press_keys(fixture, test_source, *k);
|
||||
}
|
||||
|
||||
gtk_im_context_focus_in(fixture->context);
|
||||
|
||||
for (auto p = test_source.next_key(keys); p.vk != 0; p = test_source.next_key(keys)) {
|
||||
press_keys(fixture, test_source, p);
|
||||
}
|
||||
|
||||
auto expectedText = g_utf16_to_utf8((gunichar2*)expected.c_str(), expected.length(), NULL, NULL, NULL);
|
||||
g_assert_cmpstr(ibus_im_test_get_text(fixture->ibuscontext), ==, expectedText);
|
||||
}
|
||||
|
||||
void
|
||||
print_usage() {
|
||||
printf("Usage: %s --directory <keyboarddir> test1 [test2 ...]", g_get_prgname());
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[]) {
|
||||
gtk_init(&argc, &argv);
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
g_test_set_nonfatal_assertions();
|
||||
|
||||
if (argc < 4) {
|
||||
print_usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (strcmp(argv[1], "--directory") != 0) {
|
||||
print_usage();
|
||||
return 2;
|
||||
}
|
||||
|
||||
char *directory = argv[2];
|
||||
int nTests = argc - 3;
|
||||
char **tests = &argv[3];
|
||||
|
||||
// Add tests
|
||||
for (int i = 0; i < nTests; i++)
|
||||
{
|
||||
auto filename = tests[i];
|
||||
if (strstr(filename, ".kmx") || strstr(filename, ".kmn")) {
|
||||
filename[strlen(filename) - 4] = '\0';
|
||||
}
|
||||
auto file = g_file_new_for_commandline_arg(filename);
|
||||
auto testfilebase = g_file_get_basename(file);
|
||||
auto testname = g_string_new(NULL);
|
||||
g_string_append_printf(testname, "/%s", testfilebase);
|
||||
auto testfile = g_file_new_build_filename(directory, testfilebase, NULL);
|
||||
g_test_add(
|
||||
testname->str, IBusKeymanTestsFixture, g_file_get_parse_name(testfile), ibus_keyman_tests_fixture_set_up, test_source,
|
||||
ibus_keyman_tests_fixture_tear_down);
|
||||
g_object_unref(file);
|
||||
g_object_unref(testfile);
|
||||
g_string_free(testname, TRUE);
|
||||
}
|
||||
|
||||
// Run tests
|
||||
int retVal = g_test_run();
|
||||
|
||||
test_module_unuse(module);
|
||||
return retVal;
|
||||
}
|
||||
64
linux/ibus-keyman/tests/testmodule.c
Normal file
64
linux/ibus-keyman/tests/testmodule.c
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
#include "ibusimcontext.h"
|
||||
#include <linux/input-event-codes.h>
|
||||
#include <glib-object.h>
|
||||
#include <glib.h>
|
||||
#include <ibus.h>
|
||||
#include "testmodule.h"
|
||||
|
||||
static gboolean
|
||||
test_module_load(GTypeModule *module) {
|
||||
TestModule *test_module = TEST_MODULE(module);
|
||||
|
||||
test_module->register_func(module);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
test_module_unload(GTypeModule *module) {
|
||||
}
|
||||
|
||||
static void
|
||||
test_module_class_init(TestModuleClass *class) {
|
||||
GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS(class);
|
||||
|
||||
module_class->load = test_module_load;
|
||||
module_class->unload = test_module_unload;
|
||||
}
|
||||
|
||||
static GType
|
||||
test_module_get_type(void) {
|
||||
static GType object_type = 0;
|
||||
|
||||
if (!object_type) {
|
||||
static const GTypeInfo object_info = {
|
||||
sizeof(TestModuleClass), (GBaseInitFunc)NULL,
|
||||
(GBaseFinalizeFunc)NULL, (GClassInitFunc)test_module_class_init,
|
||||
(GClassFinalizeFunc)NULL, NULL,
|
||||
sizeof(TestModule), 0,
|
||||
(GInstanceInitFunc)NULL, NULL,
|
||||
};
|
||||
object_type = g_type_register_static(G_TYPE_TYPE_MODULE, "TestModule", &object_info, 0);
|
||||
}
|
||||
return object_type;
|
||||
}
|
||||
|
||||
GTypeModule *
|
||||
test_module_new(TestModuleRegisterFunc register_func) {
|
||||
TestModule *test_module = g_object_new(TEST_TYPE_MODULE, NULL);
|
||||
GTypeModule *module = G_TYPE_MODULE(test_module);
|
||||
|
||||
test_module->register_func = register_func;
|
||||
|
||||
/* Register the types initially */
|
||||
g_type_module_set_name(module, "FOO");
|
||||
g_type_module_use(module);
|
||||
//g_type_module_unuse(module);
|
||||
|
||||
return G_TYPE_MODULE(module);
|
||||
}
|
||||
|
||||
void
|
||||
test_module_unuse(GTypeModule *test_module) {
|
||||
g_type_module_unuse(test_module);
|
||||
}
|
||||
33
linux/ibus-keyman/tests/testmodule.h
Normal file
33
linux/ibus-keyman/tests/testmodule.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef __TESTMODULE__
|
||||
#define __TESTMODULE__
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _TestModule TestModule;
|
||||
typedef struct _TestModuleClass TestModuleClass;
|
||||
|
||||
#define TEST_TYPE_MODULE (test_module_get_type())
|
||||
#define TEST_MODULE(module) (G_TYPE_CHECK_INSTANCE_CAST((module), TEST_TYPE_MODULE, TestModule))
|
||||
#define TEST_MODULE_CLASS(class) (G_TYPE_CHECK_CLASS_CAST((class), TEST_TYPE_MODULE, TestModuleClass))
|
||||
#define TEST_IS_MODULE(module) (G_TYPE_CHECK_INSTANCE_TYPE((module), TEST_TYPE_MODULE))
|
||||
#define TEST_IS_MODULE_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE((class), TEST_TYPE_MODULE))
|
||||
#define TEST_MODULE_GET_CLASS(module) (G_TYPE_INSTANCE_GET_CLASS((module), TEST_TYPE_MODULE, TestModuleClass))
|
||||
typedef void (*TestModuleRegisterFunc)(GTypeModule* module);
|
||||
|
||||
struct _TestModule {
|
||||
GTypeModule parent_instance;
|
||||
|
||||
TestModuleRegisterFunc register_func;
|
||||
};
|
||||
|
||||
struct _TestModuleClass {
|
||||
GTypeModuleClass parent_class;
|
||||
};
|
||||
|
||||
static GType test_module_get_type(void);
|
||||
GTypeModule* test_module_new(TestModuleRegisterFunc register_func);
|
||||
void test_module_unuse(GTypeModule* test_module);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __TESTMODULE__ */
|
||||
Loading…
Add table
Reference in a new issue